2using System.Collections.Generic;
5using System.Threading.Tasks;
45 private readonly SemaphoreSlim synchObj =
new SemaphoreSlim(1);
47 private string folderPath;
48 private string fileFilter;
63 this.timer?.Dispose();
66 await
FilesModule.StopSynchronization(this.folderPath);
68 await base.DestroyAsync();
74 [Page(2,
"File System", 100)]
75 [Header(3,
"Folder:")]
76 [ToolTip(4,
"Full path to folder (on host).")]
79 get => this.folderPath;
82 if (this.folderPath != value)
84 this.folderPath = value;
85 this.CheckSynchronization();
93 [Page(2,
"File System", 100)]
94 [Header(7,
"Synchronization Mode:")]
95 [ToolTip(8,
"If, and how, files in the folder (or subfolders) will be synchronized.")]
100 [Text(
TextPosition.AfterField, 16,
"You can add default script templates to be used for files found, by adding string-valued meta-data tags to the node, where the meta-data key names correspond to file extensions.")]
103 get => this.synchronizationOptions;
106 if (this.synchronizationOptions != value)
108 this.synchronizationOptions = value;
109 this.CheckSynchronization();
117 [Page(2,
"File System", 100)]
118 [Header(5,
"File Filter:")]
119 [ToolTip(6,
"You can limit the files to be monitored using a file filter. If no filter is provided, all files within the scope will be monitored.")]
122 get => this.fileFilter;
125 if (this.fileFilter != value)
127 this.fileFilter = value;
128 this.CheckSynchronization();
163 private void CheckSynchronization()
165 this.timer?.Dispose();
168 this.timer =
new Timer(this.DelayedCheckSynchronization,
null, 500, Timeout.Infinite);
176 this.timer?.Dispose();
179 return this.DelayedCheckSynchronization();
182 private void DelayedCheckSynchronization(
object P)
188 await this.DelayedCheckSynchronization();
197 private Task DelayedCheckSynchronization()
202 internal async
void Watcher_Error(
object Sender, ErrorEventArgs e)
214 internal async
void Watcher_Renamed(
object Sender, RenamedEventArgs e)
218 await this.OnRenamed(e.OldFullPath, e.FullPath);
226 internal async
void Watcher_Deleted(
object Sender, FileSystemEventArgs e)
230 await this.OnDeleted(e.FullPath);
238 internal async
void Watcher_Created(
object Sender, FileSystemEventArgs e)
242 await this.OnCreated(e.FullPath);
250 internal async
void Watcher_Changed(
object Sender, FileSystemEventArgs e)
254 if (e.ChangeType == WatcherChangeTypes.Changed)
255 await this.OnChanged(e.FullPath,
null);
263 internal async Task SynchFolder()
265 await this.SynchFolder(this.synchronizationOptions, this.fileFilter,
null);
273 new KeyValuePair<string, object>(
"Folder", this.folderPath),
274 new KeyValuePair<string, object>(
"Node ID", this.
NodeId));
276 if (!(Statistics is
null))
277 await Statistics.
Start();
280 DirectoryInfo DirInfo =
new DirectoryInfo(this.folderPath);
281 FileInfo[] Files = DirInfo.GetFiles(
string.IsNullOrEmpty(Filter) ?
"*.*" : this.
FileFilter,
283 SearchOption.AllDirectories : SearchOption.TopDirectoryOnly);
285 foreach (FileInfo File
in Files)
289 await this.OnChanged(File.FullName, Statistics);
297 Dictionary<string, Guid> ObjectIdsByPath =
new Dictionary<string, Guid>();
298 LinkedList<Tuple<string, INode, INode>> ToCheck =
new LinkedList<Tuple<string, INode, INode>>();
299 ToCheck.AddLast(
new Tuple<string, INode, INode>(
null,
null,
this));
301 while (!(ToCheck.First is
null))
303 Tuple<string, INode, INode> P = ToCheck.First.Value;
304 string ParentPath = P.Item1;
306 INode Node = P.Item3;
308 ToCheck.RemoveFirst();
317 if (!(Statistics is
null))
330 if (!(Statistics is
null))
350 if (!(Statistics is
null))
351 await Statistics.
Error(ex);
355 new KeyValuePair<string, object>(
"Folder", this.folderPath),
356 new KeyValuePair<string, object>(
"Node ID", this.
NodeId));
361 if (!(Statistics is
null))
362 await Statistics.
Done();
366 new KeyValuePair<string, object>(
"Folder", this.folderPath),
367 new KeyValuePair<string, object>(
"Node ID", this.
NodeId));
373 if (!Path.StartsWith(
this.folderPath, StringComparison.InvariantCultureIgnoreCase))
376 Path = Path.Substring(this.folderPath.Length);
378 if (Path.StartsWith(directorySeparator))
379 Path = Path.Substring(1);
381 Dictionary<string, string> DefaultTemplates =
new Dictionary<string, string>();
383 string SubPath = this.folderPath;
392 if (Tag.
Value is
string s3)
393 DefaultTemplates[Tag.
Name] = s3;
397 while (!
string.IsNullOrEmpty(Path))
399 i = Path.IndexOf(System.IO.Path.DirectorySeparatorChar);
408 s = Path.Substring(0, i);
409 Path = Path.Substring(i + 1);
412 s2 = System.IO.Path.Combine(SubPath, s);
425 if (Tag.
Value is
string s3)
426 DefaultTemplates[Tag.
Name] = s3;
430 if (!(Statistics is
null))
442 if (!
string.IsNullOrEmpty(Path))
445 if (!(Statistics is
null))
457 if (!CreateIfNecessary)
460 if (
string.IsNullOrEmpty(Path) && File.Exists(s2))
462 string FileExtension = System.IO.Path.GetExtension(s2);
463 if (!
string.IsNullOrEmpty(FileExtension) && FileExtension[0] ==
'.')
464 FileExtension = FileExtension.Substring(1);
466 if (!DefaultTemplates.TryGetValue(FileExtension, out
string Template))
467 Template =
string.Empty;
473 ScriptNodeId = Template
478 if (!(Statistics is
null))
482 new KeyValuePair<string, object>(
"Folder", Node.
FolderPath),
483 new KeyValuePair<string, object>(
"Node ID", Node.
NodeId),
484 new KeyValuePair<string, object>(
"Script Node ID", Node.
ScriptNodeId));
498 if (!(Statistics is
null))
502 new KeyValuePair<string, object>(
"Folder", Node.
FolderPath),
503 new KeyValuePair<string, object>(
"Node ID", Node.
NodeId));
515 internal static string GetLocalName(
string Path)
517 string[] Parts = Path.Split(System.IO.Path.DirectorySeparatorChar);
518 int c = Parts.Length;
524 if (!
string.IsNullOrEmpty(s = Parts[c]))
530 if (!
string.IsNullOrEmpty(s = Parts[c]))
536 private static readonly
string directorySeparator =
new string(Path.DirectorySeparatorChar, 1);
538 private async Task OnCreated(
string Path)
540 await this.synchObj.WaitAsync();
543 INode Node = await this.FindNodeLocked(Path,
true,
null);
544 await this.ExecuteAssociatedScript(Node);
548 this.synchObj.Release();
554 await this.synchObj.WaitAsync();
557 INode Node = await this.FindNodeLocked(Path,
true, Statistics);
558 await this.ExecuteAssociatedScript(Node);
562 this.synchObj.Release();
566 private async Task ExecuteAssociatedScript(
INode Node)
571 SensorData.FieldType.Momentary,
null, DateTime.MinValue, DateTime.MaxValue,
574 ScriptReferenceNode.NewMomentaryValues(e.Fields);
575 return Task.CompletedTask;
587 private async Task OnRenamed(
string OldPath,
string NewPath)
589 await this.synchObj.WaitAsync();
592 INode OldNode = await this.FindNodeLocked(OldPath,
false,
null);
593 INode NewNode = await this.FindNodeLocked(NewPath,
true,
null);
595 if (OldNode is
null || OldNode.
NodeId == NewNode.
NodeId)
599 NewScriptReferenceNode.ScriptNodeId = OldScriptReferenceNode.ScriptNodeId;
602 NewVirtualNode.MetaData = OldVirtualNode.MetaData;
606 NewProvisionedMeteringNode.OwnerAddress = OldProvisionedMeteringNode.OwnerAddress;
607 NewProvisionedMeteringNode.Public = OldProvisionedMeteringNode.Public;
608 NewProvisionedMeteringNode.Provisioned = OldProvisionedMeteringNode.Provisioned;
613 NewMetaMeteringNode.Name = OldMetaMeteringNode.Name;
614 NewMetaMeteringNode.Class = OldMetaMeteringNode.Class;
615 NewMetaMeteringNode.SerialNumber = OldMetaMeteringNode.SerialNumber;
616 NewMetaMeteringNode.MeterNumber = OldMetaMeteringNode.MeterNumber;
617 NewMetaMeteringNode.MeterLocation = OldMetaMeteringNode.MeterLocation;
618 NewMetaMeteringNode.ManufacturerDomain = OldMetaMeteringNode.ManufacturerDomain;
619 NewMetaMeteringNode.Model = OldMetaMeteringNode.Model;
620 NewMetaMeteringNode.Version = OldMetaMeteringNode.Version;
621 NewMetaMeteringNode.ProductUrl = OldMetaMeteringNode.ProductUrl;
622 NewMetaMeteringNode.Country = OldMetaMeteringNode.Country;
623 NewMetaMeteringNode.Region = OldMetaMeteringNode.Region;
624 NewMetaMeteringNode.City = OldMetaMeteringNode.City;
625 NewMetaMeteringNode.Street = OldMetaMeteringNode.Street;
626 NewMetaMeteringNode.StreetNr = OldMetaMeteringNode.StreetNr;
627 NewMetaMeteringNode.Building = OldMetaMeteringNode.Building;
628 NewMetaMeteringNode.Apartment = OldMetaMeteringNode.Apartment;
629 NewMetaMeteringNode.Room = OldMetaMeteringNode.Room;
630 NewMetaMeteringNode.Latitude = OldMetaMeteringNode.Latitude;
631 NewMetaMeteringNode.Longitude = OldMetaMeteringNode.Longitude;
632 NewMetaMeteringNode.Altitude = OldMetaMeteringNode.Altitude;
637 if (!(OldNode.
Parent is
null))
643 new KeyValuePair<string, object>(
"Old Node ID", OldNode.
NodeId),
644 new KeyValuePair<string, object>(
"New Node ID", NewNode.
NodeId));
648 this.synchObj.Release();
652 private async Task OnDeleted(
string Path)
654 await this.synchObj.WaitAsync();
657 INode Node = await this.FindNodeLocked(Path,
false,
null);
661 if (!(Node.
Parent is
null))
667 new KeyValuePair<string, object>(
"Node ID", Node.
NodeId));
671 this.synchObj.Release();
678 public override Task<IEnumerable<ICommand>>
Commands => this.GetCommands();
683 private async Task<IEnumerable<ICommand>> GetCommands()
685 List<ICommand>
Commands =
new List<ICommand>();
686 Commands.AddRange(await base.Commands);
688 Commands.Add(
new SynchronizeFolder(
this));
Static class managing the application event log. Applications and services log events on this static ...
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.
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.
Manages a chat sensor data readout request.
Contains personal sensor data.
Contains information about a language.
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 ...
Generates basic statistics around a folder synchronization process.
async Task FileDeleted(string Folder, string FileName)
File node deleted from topology.
async Task FileFound(string Folder, string FileName)
File found, corresponding to node in topology.
async Task FileAdded(string Folder, string FileName)
File node added to topology.
async Task FolderAdded(string Folder, string SubFolder)
Subfolder node added to topology.
async Task Start()
Synchronization starts.
async Task Done()
Synchronization has completed.
async Task Error(Exception ex)
An exception has occurred during synchronization.
async Task FolderDeleted(string Folder, string SubFolder)
Subfolder node deleted from topology.
async Task FolderFound(string Folder, string SubFolder)
Subfolder found, corresponding to node in topology.
Represents a file in the file system.
string FolderPath
Full path to folder.
Module maintaining active file system watchers.
Represents a file folder in the file system.
override Task< IEnumerable< ICommand > > Commands
Available command objects. If no commands are available, null is returned.
override async Task DestroyAsync()
Destroys the node. If it is a child to a parent node, it is removed from the parent first.
override 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...
override 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 ...
string FolderPath
Full path to folder.
override Task< string > GetTypeNameAsync(Language Language)
Gets the type name of the node.
FolderNode()
Represents a file folder in the file system.
Task Synchronize()
Synchronizes folder, subfolders, files and nodes.
string FileFilter
File filter to monitor
Represents a subfolder in the file system.
string FolderPath
Full path to folder.
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.
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.
Class for the root node of the Metering topology.
Base class for all provisioned metering nodes.
Node referencing a script node.
string ScriptNodeId
ID of node containing script defining node.
override async Task StartReadout(ISensorReadout Request, bool DoneAfter)
Starts the readout of the sensor.
Contains information about an error on a thing
string ErrorMessage
Error message.
Virtual node, that can be used as a placeholder for services.
MetaDataValue[] MetaData
Meta-data attached to virtual node.
Interface for nodes that are published through the concentrator interface.
Task AddAsync(INode Child)
Adds a new child to the node.
Task DestroyAsync()
Destroys the node. If it is a child to a parent node, it is removed from the parent first.
Task UpdateAsync()
Updates the node (in persisted storage).
Task< IEnumerable< INode > > ChildNodes
Child nodes. If no child nodes are available, null is returned.
Task< bool > RemoveAsync(INode Child)
Removes a child from the node.
INode Parent
Parent Node, or null if a root node.
string LogId
If provided, an ID for the node, as it would appear or be used in system logs. Can be null,...
TextPosition
Where the instructions are to be place.
SynchronizationOptions
How a folder will synchronize nodes with contents of folders.