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();
150 return Task.FromResult(
162 return Task.FromResult(
168 private void CheckSynchronization()
170 this.timer?.Dispose();
173 this.timer =
new Timer(this.DelayedCheckSynchronization,
null, 500, Timeout.Infinite);
181 this.timer?.Dispose();
184 return this.DelayedCheckSynchronization();
187 private void DelayedCheckSynchronization(
object P)
193 await this.DelayedCheckSynchronization();
202 private Task DelayedCheckSynchronization()
207 internal async
void Watcher_Error(
object Sender, ErrorEventArgs e)
219 internal async
void Watcher_Renamed(
object Sender, RenamedEventArgs e)
223 await this.OnRenamed(e.OldFullPath, e.FullPath);
231 internal async
void Watcher_Deleted(
object Sender, FileSystemEventArgs e)
235 await this.OnDeleted(e.FullPath);
243 internal async
void Watcher_Created(
object Sender, FileSystemEventArgs e)
247 await this.OnCreated(e.FullPath);
255 internal async
void Watcher_Changed(
object Sender, FileSystemEventArgs e)
259 if (e.ChangeType == WatcherChangeTypes.Changed)
260 await this.OnChanged(e.FullPath,
null);
268 internal async Task SynchFolder()
270 await this.SynchFolder(this.synchronizationOptions, this.fileFilter,
null);
278 new KeyValuePair<string, object>(
"Folder", this.folderPath),
279 new KeyValuePair<string, object>(
"Node ID", this.
NodeId));
281 if (!(Statistics is
null))
282 await Statistics.
Start();
285 DirectoryInfo DirInfo =
new DirectoryInfo(this.folderPath);
286 FileInfo[] Files = DirInfo.GetFiles(
string.IsNullOrEmpty(Filter) ?
"*.*" : this.
FileFilter,
288 SearchOption.AllDirectories : SearchOption.TopDirectoryOnly);
290 foreach (FileInfo File
in Files)
294 await this.OnChanged(File.FullName, Statistics);
302 Dictionary<string, Guid> ObjectIdsByPath =
new Dictionary<string, Guid>();
303 LinkedList<Tuple<string, INode, INode>> ToCheck =
new LinkedList<Tuple<string, INode, INode>>();
304 ToCheck.AddLast(
new Tuple<string, INode, INode>(
null,
null,
this));
306 while (!(ToCheck.First is
null))
308 Tuple<string, INode, INode> P = ToCheck.First.Value;
309 string ParentPath = P.Item1;
311 INode Node = P.Item3;
313 ToCheck.RemoveFirst();
322 if (!(Statistics is
null))
335 if (!(Statistics is
null))
355 if (!(Statistics is
null))
356 await Statistics.
Error(ex);
360 new KeyValuePair<string, object>(
"Folder", this.folderPath),
361 new KeyValuePair<string, object>(
"Node ID", this.
NodeId));
366 if (!(Statistics is
null))
367 await Statistics.
Done();
371 new KeyValuePair<string, object>(
"Folder", this.folderPath),
372 new KeyValuePair<string, object>(
"Node ID", this.
NodeId));
378 if (!Path.StartsWith(
this.folderPath, StringComparison.InvariantCultureIgnoreCase))
381 Path = Path.Substring(this.folderPath.Length);
383 if (Path.StartsWith(directorySeparator))
384 Path = Path.Substring(1);
386 Dictionary<string, string> DefaultTemplates =
new Dictionary<string, string>();
388 string SubPath = this.folderPath;
397 if (Tag.
Value is
string s3)
398 DefaultTemplates[Tag.
Name] = s3;
402 while (!
string.IsNullOrEmpty(Path))
404 i = Path.IndexOf(
System.IO.Path.DirectorySeparatorChar);
413 s = Path.Substring(0, i);
414 Path = Path.Substring(i + 1);
417 s2 =
System.IO.Path.Combine(SubPath, s);
430 if (Tag.
Value is
string s3)
431 DefaultTemplates[Tag.
Name] = s3;
435 if (!(Statistics is
null))
447 if (!
string.IsNullOrEmpty(Path))
450 if (!(Statistics is
null))
462 if (!CreateIfNecessary)
465 if (
string.IsNullOrEmpty(Path) && File.Exists(s2))
467 string FileExtension =
System.IO.Path.GetExtension(s2);
468 if (!
string.IsNullOrEmpty(FileExtension) && FileExtension[0] ==
'.')
469 FileExtension = FileExtension.Substring(1);
471 if (!DefaultTemplates.TryGetValue(FileExtension, out
string Template))
472 Template =
string.Empty;
478 ScriptNodeId = Template
483 if (!(Statistics is
null))
487 new KeyValuePair<string, object>(
"Folder", Node.
FolderPath),
488 new KeyValuePair<string, object>(
"Node ID", Node.
NodeId),
489 new KeyValuePair<string, object>(
"Script Node ID", Node.
ScriptNodeId));
503 if (!(Statistics is
null))
507 new KeyValuePair<string, object>(
"Folder", Node.
FolderPath),
508 new KeyValuePair<string, object>(
"Node ID", Node.
NodeId));
520 internal static string GetLocalName(
string Path)
522 string[] Parts = Path.Split(
System.IO.Path.DirectorySeparatorChar);
523 int c = Parts.Length;
529 if (!
string.IsNullOrEmpty(s = Parts[c]))
535 if (!
string.IsNullOrEmpty(s = Parts[c]))
541 private static readonly
string directorySeparator =
new string(Path.DirectorySeparatorChar, 1);
543 private async Task OnCreated(
string Path)
545 await this.synchObj.WaitAsync();
548 INode Node = await this.FindNodeLocked(Path,
true,
null);
549 await this.ExecuteAssociatedScript(Node);
553 this.synchObj.Release();
559 await this.synchObj.WaitAsync();
562 INode Node = await this.FindNodeLocked(Path,
true, Statistics);
563 await this.ExecuteAssociatedScript(Node);
567 this.synchObj.Release();
571 private async Task ExecuteAssociatedScript(
INode Node)
576 SensorData.FieldType.Momentary,
null, DateTime.MinValue, DateTime.MaxValue,
579 return ScriptReferenceNode.NewMomentaryValues(e.Fields);
591 private async Task OnRenamed(
string OldPath,
string NewPath)
593 await this.synchObj.WaitAsync();
596 INode OldNode = await this.FindNodeLocked(OldPath,
false,
null);
597 INode NewNode = await this.FindNodeLocked(NewPath,
true,
null);
599 if (OldNode is
null || OldNode.
NodeId == NewNode.
NodeId)
603 NewScriptReferenceNode.ScriptNodeId = OldScriptReferenceNode.ScriptNodeId;
606 NewVirtualNode.MetaData = OldVirtualNode.MetaData;
610 NewProvisionedMeteringNode.OwnerAddress = OldProvisionedMeteringNode.OwnerAddress;
611 NewProvisionedMeteringNode.Public = OldProvisionedMeteringNode.Public;
612 NewProvisionedMeteringNode.Provisioned = OldProvisionedMeteringNode.Provisioned;
617 NewMetaMeteringNode.Name = OldMetaMeteringNode.Name;
618 NewMetaMeteringNode.Class = OldMetaMeteringNode.Class;
619 NewMetaMeteringNode.SerialNumber = OldMetaMeteringNode.SerialNumber;
620 NewMetaMeteringNode.MeterNumber = OldMetaMeteringNode.MeterNumber;
621 NewMetaMeteringNode.MeterLocation = OldMetaMeteringNode.MeterLocation;
622 NewMetaMeteringNode.ManufacturerDomain = OldMetaMeteringNode.ManufacturerDomain;
623 NewMetaMeteringNode.Model = OldMetaMeteringNode.Model;
624 NewMetaMeteringNode.Version = OldMetaMeteringNode.Version;
625 NewMetaMeteringNode.ProductUrl = OldMetaMeteringNode.ProductUrl;
626 NewMetaMeteringNode.Country = OldMetaMeteringNode.Country;
627 NewMetaMeteringNode.Region = OldMetaMeteringNode.Region;
628 NewMetaMeteringNode.City = OldMetaMeteringNode.City;
629 NewMetaMeteringNode.Street = OldMetaMeteringNode.Street;
630 NewMetaMeteringNode.StreetNr = OldMetaMeteringNode.StreetNr;
631 NewMetaMeteringNode.Building = OldMetaMeteringNode.Building;
632 NewMetaMeteringNode.Apartment = OldMetaMeteringNode.Apartment;
633 NewMetaMeteringNode.Room = OldMetaMeteringNode.Room;
634 NewMetaMeteringNode.Latitude = OldMetaMeteringNode.Latitude;
635 NewMetaMeteringNode.Longitude = OldMetaMeteringNode.Longitude;
636 NewMetaMeteringNode.Altitude = OldMetaMeteringNode.Altitude;
641 if (!(OldNode.
Parent is
null))
647 new KeyValuePair<string, object>(
"Old Node ID", OldNode.
NodeId),
648 new KeyValuePair<string, object>(
"New Node ID", NewNode.
NodeId));
652 this.synchObj.Release();
656 private async Task OnDeleted(
string Path)
658 await this.synchObj.WaitAsync();
661 INode Node = await this.FindNodeLocked(Path,
false,
null);
665 if (!(Node.
Parent is
null))
671 new KeyValuePair<string, object>(
"Node ID", Node.
NodeId));
675 this.synchObj.Release();
682 public override Task<IEnumerable<ICommand>>
Commands => this.GetCommands();
687 private async Task<IEnumerable<ICommand>> GetCommands()
689 List<ICommand>
Commands =
new List<ICommand>();
690 Commands.AddRange(await base.Commands);
692 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.