Neuron®
The Neuron® is the basis for the creation of open and secure federated networks for smart societies.
Loading...
Searching...
No Matches
ProgramDataSource.cs
1using System;
2using System.Collections.Generic;
3using System.IO;
4using System.Threading.Tasks;
5using Waher.Events;
11using Waher.Things;
13
15{
19 public class ProgramDataSource : IDataSource, IDisposable
20 {
24 public const string ProgramDataSourceID = "ProgramData";
25
26 private readonly string gatewayConfigFile;
27 private FileSystemWatcher watcher;
28 private Cache<string, NodeUpdated> delayedUpdates;
29
34 {
35 this.gatewayConfigFile = Path.Combine(Gateway.AppDataFolder, Gateway.GatewayConfigLocalFileName);
36
37 this.delayedUpdates = new Cache<string, NodeUpdated>(int.MaxValue, TimeSpan.FromMinutes(1), TimeSpan.FromSeconds(15));
38 this.delayedUpdates.Removed += this.DelayedUpdates_Removed;
39
40 this.watcher = new FileSystemWatcher(Gateway.AppDataFolder, "*.*")
41 {
42 IncludeSubdirectories = true,
43 EnableRaisingEvents = true,
44 NotifyFilter =
45 NotifyFilters.Attributes |
46 NotifyFilters.CreationTime |
47 NotifyFilters.DirectoryName |
48 NotifyFilters.FileName |
49 NotifyFilters.LastAccess |
50 NotifyFilters.LastWrite |
51 NotifyFilters.Security |
52 NotifyFilters.Size
53 };
54
55 this.watcher.Changed += this.Watcher_Changed;
56 this.watcher.Created += this.Watcher_Created;
57 this.watcher.Deleted += this.Watcher_Deleted;
58 this.watcher.Renamed += this.Watcher_Renamed;
59 this.watcher.Error += this.Watcher_Error;
60 }
61
65 public void Dispose()
66 {
67 if (!(this.delayedUpdates is null))
68 {
69 this.delayedUpdates.Removed -= this.DelayedUpdates_Removed;
70 this.delayedUpdates.Dispose();
71 this.delayedUpdates = null;
72 }
73
74 if (!(this.watcher is null))
75 {
76 this.watcher.Changed -= this.Watcher_Changed;
77 this.watcher.Created -= this.Watcher_Created;
78 this.watcher.Deleted -= this.Watcher_Deleted;
79 this.watcher.Renamed -= this.Watcher_Renamed;
80 this.watcher.Error -= this.Watcher_Error;
81
82 this.watcher.Dispose();
83 this.watcher = null;
84 }
85 }
86
90 public string SourceID => ProgramDataSourceID;
91
95 public bool HasChildren => false;
96
100 public DateTime LastChanged => File.GetLastWriteTimeUtc(Gateway.AppDataFolder);
101
105 public IEnumerable<IDataSource> ChildSources => null;
106
110 public IEnumerable<INode> RootNodes => GetChildNodes(null, Gateway.AppDataFolder);
111
118 public static IEnumerable<INode> GetChildNodes(ProgramDataFolder FolderNode, string FolderPath)
119 {
120 if (FolderPath != Path.GetFullPath(FolderPath))
121 return new INode[0];
122
123 if (!FolderPath.StartsWith(Gateway.AppDataFolder))
124 return new INode[0];
125
126 SortedDictionary<string, INode> Result1 = new SortedDictionary<string, INode>(StringComparer.InvariantCultureIgnoreCase);
127 SortedDictionary<string, INode> Result2 = new SortedDictionary<string, INode>(StringComparer.InvariantCultureIgnoreCase);
128
129 if (FolderNode is null)
130 FolderNode = GetProgramDataNode(FolderPath, false, true) as ProgramDataFolder;
131
132 DirectoryInfo DirInfo = new DirectoryInfo(FolderPath);
133 if (DirInfo.Exists)
134 {
135 DirectoryInfo[] Directories = DirInfo.GetDirectories();
136 FileInfo[] Files = DirInfo.GetFiles();
137
138 foreach (DirectoryInfo Directory in Directories)
139 Result1[Directory.Name] = new ProgramDataFolder(Directory.FullName, FolderNode, Directory.CreationTimeUtc, Directory.LastWriteTimeUtc);
140
141 foreach (FileInfo File in Files)
142 Result2[File.Name] = new ProgramDataFile(File.FullName, FolderNode, File.CreationTimeUtc, File.LastAccessTimeUtc, File.Length);
143 }
144
145 List<INode> Result = new List<INode>();
146
147 Result.AddRange(Result1.Values);
148 Result.AddRange(Result2.Values);
149
150 return Result;
151 }
152
156 public event EventHandlerAsync<SourceEvent> OnEvent;
157
163 public Task<bool> CanViewAsync(RequestOrigin Caller)
164 {
165 return XmppServerModule.IsAdmin(Caller.From);
166 }
167
173 public Task<string> GetNameAsync(Language Language)
174 {
175 return Language.GetStringAsync(typeof(ProgramDataSource), 1, "Program Data Folder");
176 }
177
183 public Task<INode> GetNodeAsync(IThingReference NodeRef)
184 {
185 if (NodeRef.SourceId != this.SourceID || !string.IsNullOrEmpty(NodeRef.Partition))
186 return Task.FromResult<INode>(null);
187
188 return Task.FromResult(GetProgramDataNode(NodeRef.NodeId, true, true));
189 }
190
198 public static INode GetProgramDataNode(string Path, bool FileReference, bool FolderReference)
199 {
200 if (Path != System.IO.Path.GetFullPath(Path))
201 return null;
202
203 if (!Path.StartsWith(Gateway.AppDataFolder))
204 return null;
205
206 INode ParentNode;
207 string ParentFolder = Directory.GetParent(Path).FullName;
208 if (ParentFolder == Gateway.AppDataFolder)
209 ParentNode = null;
210 else
211 ParentNode = GetProgramDataNode(ParentFolder, false, true);
212
213 if (FileReference && File.Exists(Path))
214 return new ProgramDataFile(Path, ParentNode as ProgramDataFolder, null, null, null);
215
216 if (FolderReference && Directory.Exists(Path))
217 return new ProgramDataFolder(Path, ParentNode as ProgramDataFolder, null, null);
218
219 return null;
220 }
221
222 private Task RaiseSourceEvent(SourceEvent Event)
223 {
224 return this.OnEvent.Raise(this, Event);
225 }
226
227 private async Task DelayedUpdates_Removed(object Sender, CacheItemEventArgs<string, NodeUpdated> e)
228 {
229 switch (e.Reason)
230 {
231 case RemovedReason.Replaced:
232 case RemovedReason.Manual:
233 // Ignore
234 break;
235
236 case RemovedReason.NotUsed:
237 case RemovedReason.Old:
238 case RemovedReason.Space:
239 default:
240 await this.RaiseSourceEvent(e.Value);
241 break;
242 }
243 }
244
245 private void RaiseSourceEventDelayed(NodeUpdated Event)
246 {
247 this.delayedUpdates[Event.NodeId] = Event;
248 }
249
250 private async Task RaiseSourceEventFlushDelayed(NodeEvent Event, bool DiscardDelayed)
251 {
252 if (DiscardDelayed)
253 this.delayedUpdates.Remove(Event.NodeId);
254 else if (this.delayedUpdates.TryGetValue(Event.NodeId, out NodeUpdated PrevEvent))
255 {
256 this.delayedUpdates.Remove(Event.NodeId);
257 await this.RaiseSourceEvent(PrevEvent);
258 }
259
260 await this.RaiseSourceEvent(Event);
261 }
262
263 private void Watcher_Error(object Sender, ErrorEventArgs e)
264 {
265 Exception Exception = e.GetException();
266 Log.Exception(Exception);
267 }
268
269 private async void Watcher_Renamed(object Sender, RenamedEventArgs e)
270 {
271 try
272 {
273 if (!(this.OnEvent is null))
274 {
275 try
276 {
277 INode Node = GetProgramDataNode(e.FullPath, true, true);
278
279 if (!(Node is null))
280 {
281 await this.RaiseSourceEventFlushDelayed(await NodeUpdated.FromNode(Node, await Translator.GetDefaultLanguageAsync(),
282 RequestOrigin.Empty, e.OldFullPath), false);
283 }
284 }
285 catch (Exception ex)
286 {
287 Log.Exception(ex);
288 }
289 }
290 }
291 catch (Exception ex)
292 {
293 Log.Exception(ex);
294 }
295 }
296
297 private async void Watcher_Deleted(object Sender, FileSystemEventArgs e)
298 {
299 try
300 {
301 if (!(this.OnEvent is null))
302 {
303 try
304 {
305 INode Node = GetProgramDataNode(e.FullPath, true, true)
306 ?? new ProgramDataFile(e.FullPath, null, null, null, null);
307
308 await this.RaiseSourceEventFlushDelayed(NodeRemoved.FromNode(Node), true);
309 }
310 catch (Exception ex)
311 {
312 Log.Exception(ex);
313 }
314 }
315 }
316 catch (Exception ex)
317 {
318 Log.Exception(ex);
319 }
320 }
321
322 private async void Watcher_Created(object Sender, FileSystemEventArgs e)
323 {
324 try
325 {
326 if (!(this.OnEvent is null))
327 {
328 try
329 {
330 INode Node = GetProgramDataNode(e.FullPath, true, true);
331
332 if (!(Node is null))
333 await this.RaiseSourceEventFlushDelayed(await NodeAdded.FromNode(Node, await Translator.GetDefaultLanguageAsync(), RequestOrigin.Empty, false), true);
334 }
335 catch (Exception ex)
336 {
337 Log.Exception(ex);
338 }
339 }
340 }
341 catch (Exception ex)
342 {
343 Log.Exception(ex);
344 }
345 }
346
347 private async void Watcher_Changed(object Sender, FileSystemEventArgs e)
348 {
349 try
350 {
351 if (!(this.OnEvent is null))
352 {
353 try
354 {
355 INode Node = GetProgramDataNode(e.FullPath, true, true);
356
357 if (!(Node is null))
358 this.RaiseSourceEventDelayed(await NodeUpdated.FromNode(Node, await Translator.GetDefaultLanguageAsync(), RequestOrigin.Empty));
359 }
360 catch (Exception ex)
361 {
362 Log.Exception(ex);
363 }
364 }
365
366 if (string.Compare(e.FullPath, this.gatewayConfigFile, true) == 0)
367 await GatewayConfigSource.FileUpdated();
368 }
369 catch (Exception ex)
370 {
371 Log.Exception(ex);
372 }
373 }
374
375 }
376}
Class representing an event.
Definition: Event.cs:10
Static class managing the application event log. Applications and services log events on this static ...
Definition: Log.cs:13
static void Exception(Exception Exception, string Object, string Actor, string EventId, EventLevel Level, string Facility, string Module, params KeyValuePair< string, object >[] Tags)
Logs an exception. Event type will be determined by the severity of the exception.
Definition: Log.cs:1647
Static class managing the runtime environment of the IoT Gateway.
Definition: Gateway.cs:126
static string AppDataFolder
Application data folder.
Definition: Gateway.cs:2369
const string GatewayConfigLocalFileName
Gateway.config
Definition: Gateway.cs:130
Implements an in-memory cache.
Definition: Cache.cs:15
void Dispose()
IDisposable.Dispose
Definition: Cache.cs:74
bool Remove(KeyType Key)
Removes an item from the cache.
Definition: Cache.cs:451
bool TryGetValue(KeyType Key, out ValueType Value)
Tries to get a value from the cache.
Definition: Cache.cs:203
Event arguments for cache item removal events.
ValueType Value
Value of item that was removed.
RemovedReason Reason
Reason for removing the item.
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
Reference to a file in the ProgramData folder of the broker.
Reference to a folder in the ProgramData folder of the broker.
Data source mirroring the ProgramData folder for the broker.
Task< string > GetNameAsync(Language Language)
Gets the name of data source.
static IEnumerable< INode > GetChildNodes(ProgramDataFolder FolderNode, string FolderPath)
Gets a set of program data child nodes, for a folder.
Task< INode > GetNodeAsync(IThingReference NodeRef)
Gets the node, given a reference to it.
IEnumerable< INode > RootNodes
Root node references. If no root nodes are available, null is returned.
Task< bool > CanViewAsync(RequestOrigin Caller)
If the data source is visible to the caller.
DateTime LastChanged
When the source was last updated.
EventHandlerAsync< SourceEvent > OnEvent
Event raised when a data source event has been raised.
const string ProgramDataSourceID
Data Source ID for the ProgramData source.
ProgramDataSource()
Data source mirroring the ProgramData folder for the broker.
IEnumerable< IDataSource > ChildSources
Child sources. If no child sources are available, null is returned.
bool HasChildren
If the source has any child sources.
static INode GetProgramDataNode(string Path, bool FileReference, bool FolderReference)
Gets a node, given a program data path.
Service Module hosting the XMPP broker and its components.
Tokens available in request.
Definition: RequestOrigin.cs:9
static readonly RequestOrigin Empty
Empty request origin.
string From
Address of caller.
static Task< NodeAdded > FromNode(INode Node, Language Language, RequestOrigin Caller, bool Sniffable)
Creates an event object from a node object.
Definition: NodeAdded.cs:36
Abstract base class for all node events.
Definition: NodeEvent.cs:9
static NodeRemoved FromNode(INode Node)
Creates an event object from a node object.
Definition: NodeRemoved.cs:30
static Task< NodeUpdated > FromNode(INode Node, Language Language, RequestOrigin Caller)
Creates an event object from a node object.
Definition: NodeUpdated.cs:31
Abstract base class for all data source events.
Definition: SourceEvent.cs:13
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.
RemovedReason
Reason for removing the item.