Neuron®
The Neuron® is the basis for the creation of open and secure federated networks for smart societies.
Loading...
Searching...
No Matches
OutputSource.cs
1using System;
3using System.Threading.Tasks;
4using Waher.Events;
10using Waher.Things;
12
13namespace Waher.Output
14{
20 {
24 public const string SourceID = "Output";
25
26 private static readonly Dictionary<string, OutputNode> nodes = new Dictionary<string, OutputNode>();
27 private static Root root = null;
28 private static OutputSource instance = null;
29
30 private DateTime lastChanged;
31
36 public OutputSource()
37 {
38 this.lastChanged = RuntimeSettings.Get(SourceID + ".LastChanged", DateTime.MinValue);
39 instance ??= this;
40 }
41
45 string IDataSource.SourceID => SourceID;
46
50 public IEnumerable<IDataSource> ChildSources => null;
51
55 public bool HasChildren => false;
56
60 public DateTime LastChanged
61 {
62 get => this.lastChanged;
63 internal set
64 {
65 if (this.lastChanged != value)
66 {
67 this.lastChanged = value;
68 Task _ = RuntimeSettings.SetAsync("OutputSource.LastChanged", value);
69 }
70 }
71 }
72
78 public Task<string> GetNameAsync(Language Language)
79 {
80 return Language.GetStringAsync(typeof(OutputSource), 13, "Output");
81 }
82
88 public async Task<INode> GetNodeAsync(IThingReference NodeRef)
89 {
90 return await GetNode(NodeRef);
91 }
92
98 public static Task<OutputNode> GetNode(string NodeId)
99 {
100 return GetNode(new ThingReference(NodeId, SourceID));
101 }
102
108 public static async Task<OutputNode> GetNode(IThingReference NodeRef)
109 {
110 if (NodeRef is null || NodeRef.SourceId != SourceID || !string.IsNullOrEmpty(NodeRef.Partition))
111 return null;
112
113 lock (nodes)
114 {
115 if (nodes.TryGetValue(NodeRef.NodeId, out OutputNode Node))
116 return Node;
117 }
118
119 foreach (OutputNode Node2 in await Database.Find<OutputNode>(new FilterFieldEqualTo("NodeId", NodeRef.NodeId)))
120 {
121 lock (nodes)
122 {
123 if (nodes.TryGetValue(NodeRef.NodeId, out OutputNode Node))
124 return Node;
125 else
126 {
127 nodes[NodeRef.NodeId] = Node2;
128 return Node2;
129 }
130 }
131 }
132
133 return null;
134 }
135
136 internal static OutputNode RegisterNode(OutputNode Node)
137 {
138 if (Node.SourceId == SourceID && string.IsNullOrEmpty(Node.Partition))
139 {
140 lock (nodes)
141 {
142 if (nodes.TryGetValue(Node.NodeId, out OutputNode Node2))
143 return Node2;
144 else
145 {
146 nodes[Node.NodeId] = Node;
147 return Node;
148 }
149 }
150 }
151 else
152 return Node;
153 }
154
155 internal static OutputNode RegisterNewNodeId(OutputNode Node, string OldId)
156 {
157 if (Node.SourceId != SourceID || !string.IsNullOrEmpty(Node.Partition))
158 return Node;
159
160 lock (nodes)
161 {
162 if (!nodes.TryGetValue(OldId, out OutputNode Node2) || Node2 != Node)
163 return Node;
164
165 if (nodes.TryGetValue(Node.NodeId, out Node2))
166 return Node2;
167
168 nodes.Remove(OldId);
169 nodes[Node.NodeId] = Node;
170
171 return Node;
172 }
173 }
174
175 internal static void UnregisterNode(OutputNode Node)
176 {
177 if (Node.SourceId == SourceID && string.IsNullOrEmpty(Node.Partition))
178 {
179 lock (nodes)
180 {
181 if (nodes.TryGetValue(Node.NodeId, out OutputNode Node2) && Node == Node2)
182 nodes.Remove(Node.NodeId);
183 }
184 }
185 }
186
192 public Task<bool> CanViewAsync(RequestOrigin Caller)
193 {
194 return Task.FromResult(Caller.HasPrivilege("Source." + SourceID + ".View"));
195 }
196
200 public IEnumerable<INode> RootNodes
201 {
202 get
203 {
204 if (root is null)
205 LoadRoot().Wait();
206
207 return new INode[] { root };
208 }
209 }
210
214 public static Root Root
215 {
216 get
217 {
218 if (root is null)
219 LoadRoot().Wait();
220
221 return root;
222 }
223 }
224
225 private static async Task LoadRoot()
226 {
227 Root Result = null;
228
229 foreach (OutputNode Node in await Database.Find<OutputNode>(new FilterFieldEqualTo("ParentId", Guid.Empty)))
230 {
231 if (Node is Root Root)
232 {
233 if (Result is null)
234 Result = Root;
235 else
236 await Database.Delete(Node);
237 }
238 }
239
240 if (Result is null)
241 {
242 Result = new Root()
243 {
244 NodeId = await (await Translator.GetDefaultLanguageAsync()).GetStringAsync(typeof(Root), 1, "Root")
245 };
246
247 await Database.Insert(Result);
248
250 await NewEvent(new NodeAdded()
251 {
253 NodeType = Result.GetType().FullName,
254 Sniffable = false,
255 DisplayName = await Result.GetTypeNameAsync(Language),
256 HasChildren = Result.HasChildren,
257 ChildrenOrdered = Result.ChildrenOrdered,
258 IsReadable = Result.IsReadable,
259 IsControllable = Result.IsControllable,
260 HasCommands = Result.HasCommands,
261 ParentId = string.Empty,
262 ParentPartition = string.Empty,
263 Updated = Result.Updated,
264 State = Result.State,
265 NodeId = Result.NodeId,
266 Partition = Result.Partition,
267 LogId = NodeAdded.EmptyIfSame(Result.LogId, Result.NodeId),
268 LocalId = NodeAdded.EmptyIfSame(Result.LocalId, Result.NodeId),
269 SourceId = Result.SourceId,
270 Timestamp = DateTime.UtcNow
271 });
272 }
273
274 lock (nodes)
275 {
276 nodes[Result.NodeId] = Result;
277 }
278
279 root = Result;
280 }
281
287 public static async Task<int> DeleteOldEvents(TimeSpan MaxAge)
288 {
289 if (MaxAge <= TimeSpan.Zero)
290 throw new ArgumentException("Age must be positive.", nameof(MaxAge));
291
292 DateTime Limit = DateTime.Now.Subtract(MaxAge);
293 int NrEvents = await Database.Delete<SourceEvent>(new FilterAnd(
294 new FilterFieldEqualTo("SourceId", SourceID),
295 new FilterFieldLesserOrEqualTo("Timestamp", Limit)));
296
297 if (NrEvents > 0)
298 {
299 KeyValuePair<string, object>[] Tags = new KeyValuePair<string, object>[]
300 {
301 new KeyValuePair<string, object>("Limit", Limit),
302 new KeyValuePair<string, object>("NrEvents", NrEvents)
303 };
304
305 if (NrEvents == 1)
306 Log.Informational("Deleting 1 output event from the database.", SourceID, Tags);
307 else
308 Log.Informational("Deleting " + NrEvents.ToString() + " output events from the database.", SourceID, Tags);
309 }
310
311 return NrEvents;
312 }
313
317 public event EventHandlerAsync<SourceEvent> OnEvent = null;
318
319 internal static async Task NewEvent(SourceEvent Event)
320 {
321 await Database.Insert(Event);
322 await (instance?.OnEvent?.Raise(instance, Event) ?? Task.CompletedTask);
323 }
324
329 public static async Task<int> DeleteOrphans()
330 {
331 int Result = 0;
332
333 foreach (OutputNode Node in await Database.Find<OutputNode>())
334 {
335 if (Node.ParentId == Guid.Empty)
336 continue;
337
338 OutputNode ParentNode = await Database.TryLoadObject<OutputNode>(Node.ParentId);
339 if (ParentNode is null)
340 {
341 await Database.Delete(Node);
342 Result++;
343
344 lock (nodes)
345 {
346 nodes.Remove(Node.NodeId);
347 }
348 }
349 }
350
351 return Result;
352 }
353 }
354}
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
Class for the root node of the Output data source.
Definition: Root.cs:11
override Task< string > GetTypeNameAsync(Language Language)
Gets the type name of the node.
Definition: Root.cs:25
Base class for all output nodes.
Definition: OutputNode.cs:29
DateTime Updated
When node was last updated. If it has not been updated, value will be DateTime.MinValue.
Definition: OutputNode.cs:119
bool HasChildren
If the source has any child sources.
Definition: OutputNode.cs:582
virtual string LocalId
If provided, an ID for the node, but unique locally between siblings. Can be null,...
Definition: OutputNode.cs:562
Guid ParentId
Object ID of parent node in persistence layer.
Definition: OutputNode.cs:100
virtual bool HasCommands
If the node has registered commands or not.
Definition: OutputNode.cs:613
string NodeId
ID of node.
Definition: OutputNode.cs:142
async Task< Parameter[]> GetDisplayableParameterAraryAsync(Language Language, RequestOrigin Caller)
Gets displayable parameters.
Definition: OutputNode.cs:908
string SourceId
Optional ID of source containing node.
Definition: OutputNode.cs:158
virtual bool IsReadable
If the node can be read.
Definition: OutputNode.cs:601
virtual bool ChildrenOrdered
If the children of the node have an intrinsic order (true), or if the order is not important (false).
Definition: OutputNode.cs:595
virtual bool IsControllable
If the node can be controlled.
Definition: OutputNode.cs:607
NodeState State
Current overall state of the node.
Definition: OutputNode.cs:684
virtual string LogId
If provided, an ID for the node, as it would appear or be used in system logs. Can be null,...
Definition: OutputNode.cs:568
string Partition
Optional partition in which the Node ID is unique.
Definition: OutputNode.cs:164
Defines the Output data source. This data source contains a tree structure of output of nodes
Definition: OutputSource.cs:20
Task< string > GetNameAsync(Language Language)
Gets the name of data source.
Definition: OutputSource.cs:78
static async Task< OutputNode > GetNode(IThingReference NodeRef)
Gets a node from the Metering Topology
static async Task< int > DeleteOrphans()
Deletes orphaned nodes in the metering topology source.
static async Task< int > DeleteOldEvents(TimeSpan MaxAge)
Deletes old data source events.
IEnumerable< IDataSource > ChildSources
Child sources. If no child sources are available, null is returned.
Definition: OutputSource.cs:50
Task< bool > CanViewAsync(RequestOrigin Caller)
If the data source is visible to the caller.
IEnumerable< INode > RootNodes
Root node references. If no root nodes are available, null is returned.
bool HasChildren
If the source has any child sources.
Definition: OutputSource.cs:55
OutputSource()
Defines the Metering Topology data source. This data source contains a tree structure of persistent r...
Definition: OutputSource.cs:36
async Task< INode > GetNodeAsync(IThingReference NodeRef)
Gets the node, given a reference to it.
Definition: OutputSource.cs:88
static Task< OutputNode > GetNode(string NodeId)
Gets a node from the Metering Topology
Definition: OutputSource.cs:98
DateTime LastChanged
When the source was last updated.
Definition: OutputSource.cs:61
EventHandlerAsync< SourceEvent > OnEvent
Event raised when a data source event has been raised.
static Root Root
Root node.
const string SourceID
Source ID for the output data source.
Definition: OutputSource.cs:24
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.
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 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