Neuron®
The Neuron® is the basis for the creation of open and secure federated networks for smart societies.
Loading...
Searching...
No Matches
GroupSource.cs
1using System;
3using System.Threading.Tasks;
4using Waher.Events;
11using Waher.Things;
13
14namespace Waher.Groups
15{
20 public class GroupSource : IDataSource
21 {
25 public const string SourceID = "Groups";
26
27 private static readonly Dictionary<string, IDataSource> sources = new Dictionary<string, IDataSource>();
28 private static readonly Dictionary<string, GroupNode> nodes = new Dictionary<string, GroupNode>();
29 private static Root root = null;
30 private static GroupSource instance = null;
31 private static IDataSources dataSources = null;
32
33 private DateTime lastChanged;
34
39 public GroupSource()
40 {
41 this.lastChanged = RuntimeSettings.Get(SourceID + ".LastChanged", DateTime.MinValue);
42 instance ??= this;
43 }
44
48 string IDataSource.SourceID => SourceID;
49
53 public IEnumerable<IDataSource> ChildSources => null;
54
58 public bool HasChildren => false;
59
63 public DateTime LastChanged
64 {
65 get => this.lastChanged;
66 internal set
67 {
68 if (this.lastChanged != value)
69 {
70 this.lastChanged = value;
71 Task _ = RuntimeSettings.SetAsync("GroupSource.LastChanged", value);
72 }
73 }
74 }
75
81 public Task<string> GetNameAsync(Language Language)
82 {
83 return Language.GetStringAsync(typeof(GroupSource), 1, "Groups");
84 }
85
91 public async Task<INode> GetNodeAsync(IThingReference NodeRef)
92 {
93 return await GetNode(NodeRef);
94 }
95
101 public static Task<GroupNode> GetNode(string NodeId)
102 {
103 return GetNode(new ThingReference(NodeId, SourceID));
104 }
105
111 public static async Task<GroupNode> GetNode(IThingReference NodeRef)
112 {
113 if (NodeRef is null || NodeRef.SourceId != SourceID || !string.IsNullOrEmpty(NodeRef.Partition))
114 return null;
115
116 lock (nodes)
117 {
118 if (nodes.TryGetValue(NodeRef.NodeId, out GroupNode Node))
119 return Node;
120 }
121
122 foreach (GroupNode Node2 in await Database.Find<GroupNode>(new FilterFieldEqualTo("NodeId", NodeRef.NodeId)))
123 {
124 lock (nodes)
125 {
126 if (nodes.TryGetValue(NodeRef.NodeId, out GroupNode Node))
127 return Node;
128 else
129 {
130 nodes[NodeRef.NodeId] = Node2;
131 return Node2;
132 }
133 }
134 }
135
136 return null;
137 }
138
139 internal static GroupNode RegisterNode(GroupNode Node)
140 {
141 if (Node.SourceId == SourceID && string.IsNullOrEmpty(Node.Partition))
142 {
143 lock (nodes)
144 {
145 if (nodes.TryGetValue(Node.NodeId, out GroupNode Node2))
146 return Node2;
147 else
148 {
149 nodes[Node.NodeId] = Node;
150 return Node;
151 }
152 }
153 }
154 else
155 return Node;
156 }
157
158 internal static GroupNode RegisterNewNodeId(GroupNode Node, string OldId)
159 {
160 if (Node.SourceId != SourceID || !string.IsNullOrEmpty(Node.Partition))
161 return Node;
162
163 lock (nodes)
164 {
165 if (!nodes.TryGetValue(OldId, out GroupNode Node2) || Node2 != Node)
166 return Node;
167
168 if (nodes.TryGetValue(Node.NodeId, out Node2))
169 return Node2;
170
171 nodes.Remove(OldId);
172 nodes[Node.NodeId] = Node;
173
174 return Node;
175 }
176 }
177
178 internal static void UnregisterNode(GroupNode Node)
179 {
180 if (Node.SourceId == SourceID && string.IsNullOrEmpty(Node.Partition))
181 {
182 lock (nodes)
183 {
184 if (nodes.TryGetValue(Node.NodeId, out GroupNode Node2) && Node == Node2)
185 nodes.Remove(Node.NodeId);
186 }
187 }
188 }
189
195 public Task<bool> CanViewAsync(RequestOrigin Caller)
196 {
197 return Task.FromResult(Caller.HasPrivilege("Source." + SourceID + ".View"));
198 }
199
203 public IEnumerable<INode> RootNodes
204 {
205 get
206 {
207 if (root is null)
208 LoadRoot().Wait();
209
210 return new INode[] { root };
211 }
212 }
213
217 public static Root Root
218 {
219 get
220 {
221 if (root is null)
222 LoadRoot().Wait();
223
224 return root;
225 }
226 }
227
228 private static async Task LoadRoot()
229 {
230 Root Result = null;
231
232 foreach (GroupNode Node in await Database.Find<GroupNode>(new FilterFieldEqualTo("ParentId", Guid.Empty)))
233 {
234 if (Node is Root Root)
235 {
236 if (Result is null)
237 Result = Root;
238 else
239 await Database.Delete(Node);
240 }
241 }
242
243 if (Result is null)
244 {
245 Result = new Root()
246 {
247 NodeId = await (await Translator.GetDefaultLanguageAsync()).GetStringAsync(typeof(Root), 12, "Root")
248 };
249
250 await Database.Insert(Result);
251
253 await NewEvent(new NodeAdded()
254 {
256 NodeType = Result.GetType().FullName,
257 Sniffable = false,
258 DisplayName = await Result.GetTypeNameAsync(Language),
259 HasChildren = Result.HasChildren,
260 ChildrenOrdered = Result.ChildrenOrdered,
261 IsReadable = Result.IsReadable,
262 IsControllable = Result.IsControllable,
263 HasCommands = Result.HasCommands,
264 ParentId = string.Empty,
265 ParentPartition = string.Empty,
266 Updated = Result.Updated,
267 State = Result.State,
268 NodeId = Result.NodeId,
269 Partition = Result.Partition,
270 LogId = NodeAdded.EmptyIfSame(Result.LogId, Result.NodeId),
271 LocalId = NodeAdded.EmptyIfSame(Result.LocalId, Result.NodeId),
272 SourceId = Result.SourceId,
273 Timestamp = DateTime.UtcNow
274 });
275 }
276
277 lock (nodes)
278 {
279 nodes[Result.NodeId] = Result;
280 }
281
282 root = Result;
283 }
284
290 public static async Task<int> DeleteOldEvents(TimeSpan MaxAge)
291 {
292 if (MaxAge <= TimeSpan.Zero)
293 throw new ArgumentException("Age must be positive.", nameof(MaxAge));
294
295 DateTime Limit = DateTime.Now.Subtract(MaxAge);
296 int NrEvents = await Database.Delete<SourceEvent>(new FilterAnd(
297 new FilterFieldEqualTo("SourceId", SourceID),
298 new FilterFieldLesserOrEqualTo("Timestamp", Limit)));
299
300 if (NrEvents > 0)
301 {
302 KeyValuePair<string, object>[] Tags = new KeyValuePair<string, object>[]
303 {
304 new KeyValuePair<string, object>("Limit", Limit),
305 new KeyValuePair<string, object>("NrEvents", NrEvents)
306 };
307
308 if (NrEvents == 1)
309 Log.Informational("Deleting 1 logical group event from the database.", SourceID, Tags);
310 else
311 Log.Informational("Deleting " + NrEvents.ToString() + " logical group events from the database.", SourceID, Tags);
312 }
313
314 return NrEvents;
315 }
316
320 public event EventHandlerAsync<SourceEvent> OnEvent = null;
321
322 internal static async Task NewEvent(SourceEvent Event)
323 {
324 await Database.Insert(Event);
325 await (instance?.OnEvent?.Raise(instance, Event) ?? Task.CompletedTask);
326 }
327
332 public static async Task<int> DeleteOrphans()
333 {
334 int Result = 0;
335
336 foreach (GroupNode Node in await Database.Find<GroupNode>())
337 {
338 if (Node.ParentId == Guid.Empty)
339 continue;
340
341 GroupNode ParentNode = await Database.TryLoadObject<GroupNode>(Node.ParentId);
342 if (ParentNode is null)
343 {
344 await Database.Delete(Node);
345 Result++;
346
347 lock (nodes)
348 {
349 nodes.Remove(Node.NodeId);
350 }
351 }
352 }
353
354 return Result;
355 }
356
363 public static bool TryGetDataSource(string SourceId, out IDataSource Source)
364 {
365 lock (sources)
366 {
367 if (sources.TryGetValue(SourceId, out Source))
368 return !(Source is null);
369 }
370
371 if (dataSources is null)
372 Types.TryGetModuleParameter("Concentrator", out dataSources);
373
374 if (dataSources is null || !dataSources.TryGetDataSource(SourceId, out Source))
375 Source = null;
376
377 lock (sources)
378 {
379 sources[SourceId] = Source;
380 }
381
382 return !(Source is null);
383 }
384 }
385}
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
Base class for all group nodes.
Definition: GroupNode.cs:29
string NodeId
ID of node.
Definition: GroupNode.cs:142
NodeState State
Current overall state of the node.
Definition: GroupNode.cs:685
virtual bool IsControllable
If the node can be controlled.
Definition: GroupNode.cs:608
virtual string LocalId
If provided, an ID for the node, but unique locally between siblings. Can be null,...
Definition: GroupNode.cs:563
virtual bool IsReadable
If the node can be read.
Definition: GroupNode.cs:602
string Partition
Optional partition in which the Node ID is unique.
Definition: GroupNode.cs:164
virtual bool HasCommands
If the node has registered commands or not.
Definition: GroupNode.cs:614
Guid ParentId
Object ID of parent node in persistence layer.
Definition: GroupNode.cs:100
string SourceId
Optional ID of source containing node.
Definition: GroupNode.cs:158
virtual bool ChildrenOrdered
If the children of the node have an intrinsic order (true), or if the order is not important (false).
Definition: GroupNode.cs:596
virtual string LogId
If provided, an ID for the node, as it would appear or be used in system logs. Can be null,...
Definition: GroupNode.cs:569
DateTime Updated
When node was last updated. If it has not been updated, value will be DateTime.MinValue.
Definition: GroupNode.cs:119
async Task< Parameter[]> GetDisplayableParameterAraryAsync(Language Language, RequestOrigin Caller)
Gets displayable parameters.
Definition: GroupNode.cs:909
bool HasChildren
If the source has any child sources.
Definition: GroupNode.cs:583
Defines the Groups data source. This data source contains a tree structure of groups of nodes
Definition: GroupSource.cs:21
static async Task< GroupNode > GetNode(IThingReference NodeRef)
Gets a node from the Metering Topology
Definition: GroupSource.cs:111
Task< bool > CanViewAsync(RequestOrigin Caller)
If the data source is visible to the caller.
Definition: GroupSource.cs:195
IEnumerable< IDataSource > ChildSources
Child sources. If no child sources are available, null is returned.
Definition: GroupSource.cs:53
Task< string > GetNameAsync(Language Language)
Gets the name of data source.
Definition: GroupSource.cs:81
static async Task< int > DeleteOldEvents(TimeSpan MaxAge)
Deletes old data source events.
Definition: GroupSource.cs:290
static Root Root
Root node.
Definition: GroupSource.cs:218
GroupSource()
Defines the Metering Topology data source. This data source contains a tree structure of persistent r...
Definition: GroupSource.cs:39
bool HasChildren
If the source has any child sources.
Definition: GroupSource.cs:58
IEnumerable< INode > RootNodes
Root node references. If no root nodes are available, null is returned.
Definition: GroupSource.cs:204
DateTime LastChanged
When the source was last updated.
Definition: GroupSource.cs:64
async Task< INode > GetNodeAsync(IThingReference NodeRef)
Gets the node, given a reference to it.
Definition: GroupSource.cs:91
static bool TryGetDataSource(string SourceId, out IDataSource Source)
Tries to get a data source, given its Source ID.
Definition: GroupSource.cs:363
const string SourceID
Source ID for the groups data source.
Definition: GroupSource.cs:25
static Task< GroupNode > GetNode(string NodeId)
Gets a node from the Metering Topology
Definition: GroupSource.cs:101
EventHandlerAsync< SourceEvent > OnEvent
Event raised when a data source event has been raised.
Definition: GroupSource.cs:320
static async Task< int > DeleteOrphans()
Deletes orphaned nodes in the metering topology source.
Definition: GroupSource.cs:332
Class for the root node of the Groups data source.
Definition: Root.cs:11
override Task< string > GetTypeNameAsync(Language Language)
Gets the type name of the node.
Definition: Root.cs:25
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.
Static class that dynamically manages types and interfaces available in the runtime environment.
Definition: Types.cs:15
static bool TryGetModuleParameter(string Name, out object Value)
Tries to get a module parameter value.
Definition: Types.cs:607
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.
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 datasources that are published through the concentrator interface.
Definition: IDataSource.cs:14
Interface for collections of data sources.
Definition: IDataSources.cs:7
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