diff --git a/.editorconfig b/.editorconfig
index 273ca35ba..34f0e2ee3 100644
--- a/.editorconfig
+++ b/.editorconfig
@@ -28,3 +28,5 @@ dotnet_diagnostic.CA1861.severity = none
# SYSLIB1045: Use GeneratedRegexAttribute to generate the regular expression implementation at compile time.
dotnet_diagnostic.SYSLIB1045.severity = none
+
+csharp_style_namespace_declarations = file_scoped:error
diff --git a/src/SmiServices/Applications/DicomDirectoryProcessor/DicomDirectoryProcessor.cs b/src/SmiServices/Applications/DicomDirectoryProcessor/DicomDirectoryProcessor.cs
index 90e6e8464..0dab6f183 100644
--- a/src/SmiServices/Applications/DicomDirectoryProcessor/DicomDirectoryProcessor.cs
+++ b/src/SmiServices/Applications/DicomDirectoryProcessor/DicomDirectoryProcessor.cs
@@ -3,39 +3,38 @@
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
-namespace SmiServices.Applications.DicomDirectoryProcessor
+namespace SmiServices.Applications.DicomDirectoryProcessor;
+
+///
+/// Command line program to process a directory and write an Accession
+/// Directory message to the message exchange for each directory found
+/// that contains DICOM (*.dcm) files.
+///
+public static class DicomDirectoryProcessor
{
///
- /// Command line program to process a directory and write an Accession
- /// Directory message to the message exchange for each directory found
- /// that contains DICOM (*.dcm) files.
+ /// Main program.
///
- public static class DicomDirectoryProcessor
+ ///
+ /// Arguments. There should be exactly one argument that specified the
+ /// path to the top level directory that is be searched.
+ ///
+ [ExcludeFromCodeCoverage]
+ public static int Main(IEnumerable args)
{
- ///
- /// Main program.
- ///
- ///
- /// Arguments. There should be exactly one argument that specified the
- /// path to the top level directory that is be searched.
- ///
- [ExcludeFromCodeCoverage]
- public static int Main(IEnumerable args)
- {
- int ret = SmiCliInit
- .ParseAndRun(
- args,
- nameof(DicomDirectoryProcessor),
- OnParse
- );
- return ret;
- }
+ int ret = SmiCliInit
+ .ParseAndRun(
+ args,
+ nameof(DicomDirectoryProcessor),
+ OnParse
+ );
+ return ret;
+ }
- private static int OnParse(GlobalOptions globals, DicomDirectoryProcessorCliOptions parsedOptions)
- {
- var bootstrapper = new MicroserviceHostBootstrapper(() => new DicomDirectoryProcessorHost(globals, parsedOptions));
- int ret = bootstrapper.Main();
- return ret;
- }
+ private static int OnParse(GlobalOptions globals, DicomDirectoryProcessorCliOptions parsedOptions)
+ {
+ var bootstrapper = new MicroserviceHostBootstrapper(() => new DicomDirectoryProcessorHost(globals, parsedOptions));
+ int ret = bootstrapper.Main();
+ return ret;
}
}
diff --git a/src/SmiServices/Applications/DicomDirectoryProcessor/DicomDirectoryProcessorCliOptions.cs b/src/SmiServices/Applications/DicomDirectoryProcessor/DicomDirectoryProcessorCliOptions.cs
index 52a7487c5..47905ac4f 100644
--- a/src/SmiServices/Applications/DicomDirectoryProcessor/DicomDirectoryProcessorCliOptions.cs
+++ b/src/SmiServices/Applications/DicomDirectoryProcessor/DicomDirectoryProcessorCliOptions.cs
@@ -5,45 +5,44 @@
using System.Collections.Generic;
using System.IO;
-namespace SmiServices.Applications.DicomDirectoryProcessor
+namespace SmiServices.Applications.DicomDirectoryProcessor;
+
+public class DicomDirectoryProcessorCliOptions : CliOptions
{
- public class DicomDirectoryProcessorCliOptions : CliOptions
- {
- [Option('d', "to-process", Required = true, HelpText = "The directory to process")]
- public string ToProcess { get; set; } = null!;
+ [Option('d', "to-process", Required = true, HelpText = "The directory to process")]
+ public string ToProcess { get; set; } = null!;
- [Option('f', "directory-format", Required = false, HelpText = "The specific directory search format to use (case insensitive). Options include PACS,LIST,ZIPS and DEFAULT", Default = "Default")]
- public string? DirectoryFormat { get; set; }
+ [Option('f', "directory-format", Required = false, HelpText = "The specific directory search format to use (case insensitive). Options include PACS,LIST,ZIPS and DEFAULT", Default = "Default")]
+ public string? DirectoryFormat { get; set; }
- public DirectoryInfo? ToProcessDir
+ public DirectoryInfo? ToProcessDir
+ {
+ get
{
- get
- {
- return ToProcess == null
- ? null
- : new DirectoryInfo(ToProcess);
- }
- set => ToProcess = value?.FullName ?? throw new ArgumentNullException(nameof(value));
+ return ToProcess == null
+ ? null
+ : new DirectoryInfo(ToProcess);
}
+ set => ToProcess = value?.FullName ?? throw new ArgumentNullException(nameof(value));
+ }
- [Usage]
- public static IEnumerable Examples
+ [Usage]
+ public static IEnumerable Examples
+ {
+ get
{
- get
- {
- yield return
- new Example("Normal Scenario", new DicomDirectoryProcessorCliOptions { ToProcess = @"c:\temp\bob" });
- yield return
- new Example("Override Yaml File", new DicomDirectoryProcessorCliOptions { ToProcess = @"c:\temp\bob", YamlFile = "myconfig.yaml" });
- yield return
- new Example("Search using the PACS directory structure", new DicomDirectoryProcessorCliOptions { ToProcess = @"c:\temp\bob", DirectoryFormat = "PACS" });
- }
+ yield return
+ new Example("Normal Scenario", new DicomDirectoryProcessorCliOptions { ToProcess = @"c:\temp\bob" });
+ yield return
+ new Example("Override Yaml File", new DicomDirectoryProcessorCliOptions { ToProcess = @"c:\temp\bob", YamlFile = "myconfig.yaml" });
+ yield return
+ new Example("Search using the PACS directory structure", new DicomDirectoryProcessorCliOptions { ToProcess = @"c:\temp\bob", DirectoryFormat = "PACS" });
}
+ }
- public override string ToString()
- {
- return base.ToString() + "ToProcess: \"" + ToProcess + ", DirectoryFormat" + DirectoryFormat + "\"\n";
- }
+ public override string ToString()
+ {
+ return base.ToString() + "ToProcess: \"" + ToProcess + ", DirectoryFormat" + DirectoryFormat + "\"\n";
}
}
diff --git a/src/SmiServices/Applications/DicomDirectoryProcessor/DicomDirectoryProcessorHost.cs b/src/SmiServices/Applications/DicomDirectoryProcessor/DicomDirectoryProcessorHost.cs
index 88d3feee1..14873fc8d 100644
--- a/src/SmiServices/Applications/DicomDirectoryProcessor/DicomDirectoryProcessorHost.cs
+++ b/src/SmiServices/Applications/DicomDirectoryProcessor/DicomDirectoryProcessorHost.cs
@@ -5,102 +5,101 @@
using System.Globalization;
using System.IO;
-namespace SmiServices.Applications.DicomDirectoryProcessor
+namespace SmiServices.Applications.DicomDirectoryProcessor;
+
+///
+/// Processes directories to find those that contain DICOM files
+///
+public class DicomDirectoryProcessorHost : MicroserviceHost
{
+ private readonly DicomDirectoryProcessorCliOptions _cliOptions;
+ private readonly IDicomDirectoryFinder _ddf;
+
///
- /// Processes directories to find those that contain DICOM files
+ /// Constructor
///
- public class DicomDirectoryProcessorHost : MicroserviceHost
+ /// Common microservices options. Must contain details for an message exchange labelled as "accessionDirectories"
+ /// Configuration settings for the program
+ public DicomDirectoryProcessorHost(GlobalOptions globals, DicomDirectoryProcessorCliOptions cliOptions)
+ : base(globals)
{
- private readonly DicomDirectoryProcessorCliOptions _cliOptions;
- private readonly IDicomDirectoryFinder _ddf;
+ _cliOptions = cliOptions;
- ///
- /// Constructor
- ///
- /// Common microservices options. Must contain details for an message exchange labelled as "accessionDirectories"
- /// Configuration settings for the program
- public DicomDirectoryProcessorHost(GlobalOptions globals, DicomDirectoryProcessorCliOptions cliOptions)
- : base(globals)
+ if (!cliOptions.DirectoryFormat!.ToLower().Equals("list"))
{
- _cliOptions = cliOptions;
-
- if (!cliOptions.DirectoryFormat!.ToLower().Equals("list"))
- {
- // TODO(rkm 2020-02-12) I think we want to check this regardless of the mode
- // (bp 2020-02-13) By not doing this check on list means that the list of paths is not required to be in PACS and can be imported from anywhere
- if (!Directory.Exists(globals.FileSystemOptions!.FileSystemRoot))
- throw new ArgumentException("Cannot find the FileSystemRoot specified in the given MicroservicesOptions (" + globals.FileSystemOptions.FileSystemRoot + ")");
+ // TODO(rkm 2020-02-12) I think we want to check this regardless of the mode
+ // (bp 2020-02-13) By not doing this check on list means that the list of paths is not required to be in PACS and can be imported from anywhere
+ if (!Directory.Exists(globals.FileSystemOptions!.FileSystemRoot))
+ throw new ArgumentException("Cannot find the FileSystemRoot specified in the given MicroservicesOptions (" + globals.FileSystemOptions.FileSystemRoot + ")");
- if (!cliOptions.ToProcessDir!.Exists)
- throw new ArgumentException("Could not find directory " + cliOptions.ToProcessDir.FullName);
+ if (!cliOptions.ToProcessDir!.Exists)
+ throw new ArgumentException("Could not find directory " + cliOptions.ToProcessDir.FullName);
- if (!cliOptions.ToProcessDir.FullName.StartsWith(globals.FileSystemOptions.FileSystemRoot, true, CultureInfo.CurrentCulture))
- throw new ArgumentException("Directory parameter (" + cliOptions.ToProcessDir.FullName + ") must be below the FileSystemRoot (" + globals.FileSystemOptions.FileSystemRoot + ")");
- }
- else
- {
- if (!File.Exists(cliOptions.ToProcessDir!.FullName))
- throw new ArgumentException("Could not find accession directory list file (" + cliOptions.ToProcessDir.FullName + ")");
+ if (!cliOptions.ToProcessDir.FullName.StartsWith(globals.FileSystemOptions.FileSystemRoot, true, CultureInfo.CurrentCulture))
+ throw new ArgumentException("Directory parameter (" + cliOptions.ToProcessDir.FullName + ") must be below the FileSystemRoot (" + globals.FileSystemOptions.FileSystemRoot + ")");
+ }
+ else
+ {
+ if (!File.Exists(cliOptions.ToProcessDir!.FullName))
+ throw new ArgumentException("Could not find accession directory list file (" + cliOptions.ToProcessDir.FullName + ")");
- if (!Path.GetExtension(cliOptions.ToProcessDir.FullName).Equals(".csv"))
- throw new ArgumentException("When in 'list' mode, path to accession directory file of format .csv expected (" + cliOptions.ToProcessDir.FullName + ")");
- }
+ if (!Path.GetExtension(cliOptions.ToProcessDir.FullName).Equals(".csv"))
+ throw new ArgumentException("When in 'list' mode, path to accession directory file of format .csv expected (" + cliOptions.ToProcessDir.FullName + ")");
+ }
- switch (cliOptions.DirectoryFormat.ToLower())
- {
- case "pacs":
- Logger.Info("Creating PACS directory finder");
+ switch (cliOptions.DirectoryFormat.ToLower())
+ {
+ case "pacs":
+ Logger.Info("Creating PACS directory finder");
- _ddf = new PacsDirectoryFinder(globals.FileSystemOptions!.FileSystemRoot!,
- globals.FileSystemOptions.DicomSearchPattern!, MessageBroker.SetupProducer(globals.ProcessDirectoryOptions!.AccessionDirectoryProducerOptions!, isBatch: false));
- break;
- case "list":
- Logger.Info("Creating accession directory lister");
+ _ddf = new PacsDirectoryFinder(globals.FileSystemOptions!.FileSystemRoot!,
+ globals.FileSystemOptions.DicomSearchPattern!, MessageBroker.SetupProducer(globals.ProcessDirectoryOptions!.AccessionDirectoryProducerOptions!, isBatch: false));
+ break;
+ case "list":
+ Logger.Info("Creating accession directory lister");
- _ddf = new AccessionDirectoryLister(globals.FileSystemOptions!.FileSystemRoot!,
- globals.FileSystemOptions.DicomSearchPattern!, MessageBroker.SetupProducer(globals.ProcessDirectoryOptions!.AccessionDirectoryProducerOptions!, isBatch: false));
- break;
- case "default":
- Logger.Info("Creating basic directory finder");
+ _ddf = new AccessionDirectoryLister(globals.FileSystemOptions!.FileSystemRoot!,
+ globals.FileSystemOptions.DicomSearchPattern!, MessageBroker.SetupProducer(globals.ProcessDirectoryOptions!.AccessionDirectoryProducerOptions!, isBatch: false));
+ break;
+ case "default":
+ Logger.Info("Creating basic directory finder");
- _ddf = new BasicDicomDirectoryFinder(globals.FileSystemOptions!.FileSystemRoot!,
- globals.FileSystemOptions.DicomSearchPattern!, MessageBroker.SetupProducer(globals.ProcessDirectoryOptions!.AccessionDirectoryProducerOptions!, isBatch: false));
- break;
- case "zips":
- Logger.Info("Creating zip directory finder");
+ _ddf = new BasicDicomDirectoryFinder(globals.FileSystemOptions!.FileSystemRoot!,
+ globals.FileSystemOptions.DicomSearchPattern!, MessageBroker.SetupProducer(globals.ProcessDirectoryOptions!.AccessionDirectoryProducerOptions!, isBatch: false));
+ break;
+ case "zips":
+ Logger.Info("Creating zip directory finder");
- _ddf = new ZipDicomDirectoryFinder(globals.FileSystemOptions!.FileSystemRoot!,
- globals.FileSystemOptions.DicomSearchPattern!, MessageBroker.SetupProducer(globals.ProcessDirectoryOptions!.AccessionDirectoryProducerOptions!, isBatch: false));
- break;
- default:
- throw new ArgumentException(
- $"Could not match directory format {cliOptions.DirectoryFormat} to an directory scan implementation");
- }
+ _ddf = new ZipDicomDirectoryFinder(globals.FileSystemOptions!.FileSystemRoot!,
+ globals.FileSystemOptions.DicomSearchPattern!, MessageBroker.SetupProducer(globals.ProcessDirectoryOptions!.AccessionDirectoryProducerOptions!, isBatch: false));
+ break;
+ default:
+ throw new ArgumentException(
+ $"Could not match directory format {cliOptions.DirectoryFormat} to an directory scan implementation");
}
+ }
- ///
- /// Searches from the given directory to look for DICOM files and writes AccessionDirectoryMessages to the message exchange
- ///
- public override void Start()
+ ///
+ /// Searches from the given directory to look for DICOM files and writes AccessionDirectoryMessages to the message exchange
+ ///
+ public override void Start()
+ {
+ try
{
- try
- {
- _ddf.SearchForDicomDirectories(_cliOptions.ToProcessDir!.FullName);
- }
- catch (Exception e)
- {
- Fatal(e.Message, e);
- return;
- }
-
- Stop("Directory scan completed");
+ _ddf.SearchForDicomDirectories(_cliOptions.ToProcessDir!.FullName);
}
-
- public override void Stop(string reason)
+ catch (Exception e)
{
- _ddf.Stop();
- base.Stop(reason);
+ Fatal(e.Message, e);
+ return;
}
+
+ Stop("Directory scan completed");
+ }
+
+ public override void Stop(string reason)
+ {
+ _ddf.Stop();
+ base.Stop(reason);
}
}
diff --git a/src/SmiServices/Applications/DicomDirectoryProcessor/DirectoryFinders/AccessionDirectoryLister.cs b/src/SmiServices/Applications/DicomDirectoryProcessor/DirectoryFinders/AccessionDirectoryLister.cs
index a5043b5f1..a1320f7c8 100644
--- a/src/SmiServices/Applications/DicomDirectoryProcessor/DirectoryFinders/AccessionDirectoryLister.cs
+++ b/src/SmiServices/Applications/DicomDirectoryProcessor/DirectoryFinders/AccessionDirectoryLister.cs
@@ -5,72 +5,71 @@
using System.Linq;
using System.Text.RegularExpressions;
-namespace SmiServices.Applications.DicomDirectoryProcessor.DirectoryFinders
+namespace SmiServices.Applications.DicomDirectoryProcessor.DirectoryFinders;
+
+public class AccessionDirectoryLister : DicomDirectoryFinder
{
- public class AccessionDirectoryLister : DicomDirectoryFinder
- {
- // Regex that matches when we are at the yyyy\mm\dd\xxxxx directory level
- private static readonly Regex _accDirectoryRegex = new(@"(20\d{2}[\\\/]\d{2}[\\\/]\d{2}[\\\/][a-zA-Z0-9._-]+[\\\/]$)");
+ // Regex that matches when we are at the yyyy\mm\dd\xxxxx directory level
+ private static readonly Regex _accDirectoryRegex = new(@"(20\d{2}[\\\/]\d{2}[\\\/]\d{2}[\\\/][a-zA-Z0-9._-]+[\\\/]$)");
- public AccessionDirectoryLister(string fileSystemRoot, IFileSystem fileSystem, string dicomSearchPattern, IProducerModel directoriesProducerModel)
- : base(fileSystemRoot, fileSystem, dicomSearchPattern, directoriesProducerModel) { }
+ public AccessionDirectoryLister(string fileSystemRoot, IFileSystem fileSystem, string dicomSearchPattern, IProducerModel directoriesProducerModel)
+ : base(fileSystemRoot, fileSystem, dicomSearchPattern, directoriesProducerModel) { }
- public AccessionDirectoryLister(string fileSystemRoot, string dicomSearchPattern, IProducerModel directoriesProducerModel)
- : this(fileSystemRoot, new FileSystem(), dicomSearchPattern, directoriesProducerModel) { }
+ public AccessionDirectoryLister(string fileSystemRoot, string dicomSearchPattern, IProducerModel directoriesProducerModel)
+ : this(fileSystemRoot, new FileSystem(), dicomSearchPattern, directoriesProducerModel) { }
- public override void SearchForDicomDirectories(string accessionsList)
+ public override void SearchForDicomDirectories(string accessionsList)
+ {
+ Logger.Info($"Starting accession directory path listing from: {accessionsList}");
+ IsProcessing = true;
+ TotalSent = 0;
+
+ using var reader = FileSystem.File.OpenText(accessionsList);
+ while (!reader.EndOfStream && !TokenSource.IsCancellationRequested)
{
- Logger.Info($"Starting accession directory path listing from: {accessionsList}");
- IsProcessing = true;
- TotalSent = 0;
- using var reader = FileSystem.File.OpenText(accessionsList);
- while (!reader.EndOfStream && !TokenSource.IsCancellationRequested)
+ var accessionDirectory = reader.ReadLine()?.Replace(",", "");
+
+ if (accessionDirectory is null || !_accDirectoryRegex.IsMatch(accessionDirectory))
{
+ Logger.Warn($"This path does not point to an accession directory: ({accessionDirectory}), continuing");
+ continue;
+ }
- var accessionDirectory = reader.ReadLine()?.Replace(",", "");
-
- if (accessionDirectory is null || !_accDirectoryRegex.IsMatch(accessionDirectory))
- {
- Logger.Warn($"This path does not point to an accession directory: ({accessionDirectory}), continuing");
- continue;
- }
-
- if (!FileSystem.Directory.Exists(accessionDirectory))
- {
- Logger.Warn($"Can not find {accessionDirectory}, continuing");
- continue;
- }
-
- var dirInfo = FileSystem.DirectoryInfo.New(accessionDirectory);
- IEnumerable fileEnumerator;
-
- try
- {
- fileEnumerator = dirInfo.EnumerateFiles(SearchPattern);
- }
- catch (Exception e)
- {
- Logger.Error($"Could not enumerate files: {e.Message}");
- continue;
- }
-
- if (fileEnumerator.FirstOrDefault() == null)
- {
- Logger.Warn(
- $"Could not find dicom files in the given accession directory ({accessionDirectory}), skipping");
- continue;
- }
-
- Logger.Debug($"Sending message ({accessionDirectory})");
- FoundNewDicomDirectory(accessionDirectory.Remove(0, FileSystemRoot.Length));
+ if (!FileSystem.Directory.Exists(accessionDirectory))
+ {
+ Logger.Warn($"Can not find {accessionDirectory}, continuing");
+ continue;
}
- IsProcessing = false;
+ var dirInfo = FileSystem.DirectoryInfo.New(accessionDirectory);
+ IEnumerable fileEnumerator;
- Logger.Info("Reading from list finished");
- Logger.Info($"Total messages sent: {TotalSent}");
+ try
+ {
+ fileEnumerator = dirInfo.EnumerateFiles(SearchPattern);
+ }
+ catch (Exception e)
+ {
+ Logger.Error($"Could not enumerate files: {e.Message}");
+ continue;
+ }
+
+ if (fileEnumerator.FirstOrDefault() == null)
+ {
+ Logger.Warn(
+ $"Could not find dicom files in the given accession directory ({accessionDirectory}), skipping");
+ continue;
+ }
+
+ Logger.Debug($"Sending message ({accessionDirectory})");
+ FoundNewDicomDirectory(accessionDirectory.Remove(0, FileSystemRoot.Length));
}
+
+ IsProcessing = false;
+
+ Logger.Info("Reading from list finished");
+ Logger.Info($"Total messages sent: {TotalSent}");
}
}
diff --git a/src/SmiServices/Applications/DicomDirectoryProcessor/DirectoryFinders/BasicDicomDirectoryFinder.cs b/src/SmiServices/Applications/DicomDirectoryProcessor/DirectoryFinders/BasicDicomDirectoryFinder.cs
index 95fc93a4e..75522b8e3 100644
--- a/src/SmiServices/Applications/DicomDirectoryProcessor/DirectoryFinders/BasicDicomDirectoryFinder.cs
+++ b/src/SmiServices/Applications/DicomDirectoryProcessor/DirectoryFinders/BasicDicomDirectoryFinder.cs
@@ -6,121 +6,120 @@
using System.Linq;
using System.Text;
-namespace SmiServices.Applications.DicomDirectoryProcessor.DirectoryFinders
-{
- public class BasicDicomDirectoryFinder : DicomDirectoryFinder
- {
- ///
- /// True - Always go to bottom of directory structure
- /// False - If a directory contains dicom files do not enumerate it's subdirectories
- ///
- public bool AlwaysSearchSubdirectories { get; set; }
+namespace SmiServices.Applications.DicomDirectoryProcessor.DirectoryFinders;
- public BasicDicomDirectoryFinder(string fileSystemRoot, IFileSystem fileSystem, string dicomSearchPattern, IProducerModel directoriesProducerModel)
- : base(fileSystemRoot, fileSystem, dicomSearchPattern, directoriesProducerModel) { }
-
- public BasicDicomDirectoryFinder(string fileSystemRoot, string dicomSearchPattern, IProducerModel directoriesProducerModel)
- : this(fileSystemRoot, new FileSystem(), dicomSearchPattern, directoriesProducerModel) { }
+public class BasicDicomDirectoryFinder : DicomDirectoryFinder
+{
+ ///
+ /// True - Always go to bottom of directory structure
+ /// False - If a directory contains dicom files do not enumerate it's subdirectories
+ ///
+ public bool AlwaysSearchSubdirectories { get; set; }
+ public BasicDicomDirectoryFinder(string fileSystemRoot, IFileSystem fileSystem, string dicomSearchPattern, IProducerModel directoriesProducerModel)
+ : base(fileSystemRoot, fileSystem, dicomSearchPattern, directoriesProducerModel) { }
- public override void SearchForDicomDirectories(string topLevelDirectory)
- {
- Logger.Info("Starting directory scan of: " + topLevelDirectory);
- IsProcessing = true;
- TotalSent = 0;
+ public BasicDicomDirectoryFinder(string fileSystemRoot, string dicomSearchPattern, IProducerModel directoriesProducerModel)
+ : this(fileSystemRoot, new FileSystem(), dicomSearchPattern, directoriesProducerModel) { }
- if (!FileSystem.Directory.Exists(topLevelDirectory))
- throw new DirectoryNotFoundException("Could not find the top level directory at the start of the scan \"" + topLevelDirectory + "\"");
- Times = [];
- for (var i = 0; i < 6; ++i)
- Times.Add([]);
+ public override void SearchForDicomDirectories(string topLevelDirectory)
+ {
+ Logger.Info("Starting directory scan of: " + topLevelDirectory);
+ IsProcessing = true;
+ TotalSent = 0;
- var dirStack = new Stack();
- dirStack.Push(topLevelDirectory);
+ if (!FileSystem.Directory.Exists(topLevelDirectory))
+ throw new DirectoryNotFoundException("Could not find the top level directory at the start of the scan \"" + topLevelDirectory + "\"");
- var largestStackSize = 1;
+ Times = [];
+ for (var i = 0; i < 6; ++i)
+ Times.Add([]);
- while (dirStack.Count > 0 && !TokenSource.IsCancellationRequested)
- {
- Logger.Debug($"Start of loop, stack size is: {dirStack.Count}");
+ var dirStack = new Stack();
+ dirStack.Push(topLevelDirectory);
- string dir = dirStack.Pop();
- Logger.Debug($"Scanning {dir}");
+ var largestStackSize = 1;
- if (!FileSystem.Directory.Exists(dir))
- {
- // Occurs too often on the VM for us to throw and exit from here, just have to log & continue for now
- //throw new DirectoryNotFoundException("A previously seen directory can no longer be found: " + dir);
+ while (dirStack.Count > 0 && !TokenSource.IsCancellationRequested)
+ {
+ Logger.Debug($"Start of loop, stack size is: {dirStack.Count}");
- Logger.Warn($"Can no longer find {dir}, continuing");
- continue;
- }
+ string dir = dirStack.Pop();
+ Logger.Debug($"Scanning {dir}");
- // Lazy-evaluate the contents of the directory so we don't overwhelm the filesystem
- // and return on the first instance of a dicom file
+ if (!FileSystem.Directory.Exists(dir))
+ {
+ // Occurs too often on the VM for us to throw and exit from here, just have to log & continue for now
+ //throw new DirectoryNotFoundException("A previously seen directory can no longer be found: " + dir);
- Stopwatch.Restart();
- StringBuilder = new StringBuilder();
+ Logger.Warn($"Can no longer find {dir}, continuing");
+ continue;
+ }
- IDirectoryInfo dirInfo = FileSystem.DirectoryInfo.New(dir);
- LogTime(TimeLabel.NewDirInfo);
+ // Lazy-evaluate the contents of the directory so we don't overwhelm the filesystem
+ // and return on the first instance of a dicom file
- IEnumerable fileEnumerator;
- try
- {
- fileEnumerator = GetEnumerator(dirInfo);
- LogTime(TimeLabel.EnumFiles);
- }
- catch (Exception e)
- {
- Logger.Error($"Couldn't enumerate files: {e.Message}");
- continue;
- }
+ Stopwatch.Restart();
+ StringBuilder = new StringBuilder();
- bool hasDicom = fileEnumerator.FirstOrDefault() != null;
- LogTime(TimeLabel.FirstOrDef);
+ IDirectoryInfo dirInfo = FileSystem.DirectoryInfo.New(dir);
+ LogTime(TimeLabel.NewDirInfo);
- // If directory contains any DICOM files report and don't go any further
- if (hasDicom)
- {
- FoundNewDicomDirectory(dir);
- LogTime(TimeLabel.FoundNewDir);
- }
+ IEnumerable fileEnumerator;
+ try
+ {
+ fileEnumerator = GetEnumerator(dirInfo);
+ LogTime(TimeLabel.EnumFiles);
+ }
+ catch (Exception e)
+ {
+ Logger.Error($"Couldn't enumerate files: {e.Message}");
+ continue;
+ }
- if (!hasDicom || AlwaysSearchSubdirectories)
- {
- Logger.Debug($"Enumerating subdirectories of {dir}");
+ bool hasDicom = fileEnumerator.FirstOrDefault() != null;
+ LogTime(TimeLabel.FirstOrDef);
- IEnumerable dirEnumerable = FileSystem.Directory.EnumerateDirectories(dir);
- LogTime(TimeLabel.EnumDirs);
+ // If directory contains any DICOM files report and don't go any further
+ if (hasDicom)
+ {
+ FoundNewDicomDirectory(dir);
+ LogTime(TimeLabel.FoundNewDir);
+ }
- var totalSubDirs = 0;
+ if (!hasDicom || AlwaysSearchSubdirectories)
+ {
+ Logger.Debug($"Enumerating subdirectories of {dir}");
- foreach (string subDir in dirEnumerable)
- {
- dirStack.Push(subDir);
- ++totalSubDirs;
- }
+ IEnumerable dirEnumerable = FileSystem.Directory.EnumerateDirectories(dir);
+ LogTime(TimeLabel.EnumDirs);
- if (dirStack.Count > largestStackSize)
- largestStackSize = dirStack.Count;
+ var totalSubDirs = 0;
- LogTime(TimeLabel.PushDirs);
- Logger.Debug($"Found {totalSubDirs} subdirectories");
+ foreach (string subDir in dirEnumerable)
+ {
+ dirStack.Push(subDir);
+ ++totalSubDirs;
}
- Logger.Debug(StringBuilder.ToString);
+ if (dirStack.Count > largestStackSize)
+ largestStackSize = dirStack.Count;
+
+ LogTime(TimeLabel.PushDirs);
+ Logger.Debug($"Found {totalSubDirs} subdirectories");
}
- IsProcessing = false;
+ Logger.Debug(StringBuilder.ToString);
+ }
- Logger.Info("Directory scan finished");
- Logger.Info($"Total messages sent: {TotalSent}");
- Logger.Info($"Largest stack size was: {largestStackSize}");
+ IsProcessing = false;
- if (TotalSent > 0)
- Logger.Info(CalcAverages());
- }
+ Logger.Info("Directory scan finished");
+ Logger.Info($"Total messages sent: {TotalSent}");
+ Logger.Info($"Largest stack size was: {largestStackSize}");
+
+ if (TotalSent > 0)
+ Logger.Info(CalcAverages());
}
}
diff --git a/src/SmiServices/Applications/DicomDirectoryProcessor/DirectoryFinders/DicomDirectoryFinder.cs b/src/SmiServices/Applications/DicomDirectoryProcessor/DirectoryFinders/DicomDirectoryFinder.cs
index b8b7b8e49..1b13ddbc1 100644
--- a/src/SmiServices/Applications/DicomDirectoryProcessor/DirectoryFinders/DicomDirectoryFinder.cs
+++ b/src/SmiServices/Applications/DicomDirectoryProcessor/DirectoryFinders/DicomDirectoryFinder.cs
@@ -10,135 +10,134 @@
using System.Text;
using System.Threading;
-namespace SmiServices.Applications.DicomDirectoryProcessor.DirectoryFinders
+namespace SmiServices.Applications.DicomDirectoryProcessor.DirectoryFinders;
+
+///
+/// Finds directories that contain DICOM files and outputs one AccessionDirectoryMessage for each directory it finds
+/// that contains a *.dcm file (or ). It will search into subdirectories but will not search
+/// into subdirectories below any directory that does contain a *.dcm file (or ).
+///
+public abstract class DicomDirectoryFinder : IDicomDirectoryFinder
{
- ///
- /// Finds directories that contain DICOM files and outputs one AccessionDirectoryMessage for each directory it finds
- /// that contains a *.dcm file (or ). It will search into subdirectories but will not search
- /// into subdirectories below any directory that does contain a *.dcm file (or ).
- ///
- public abstract class DicomDirectoryFinder : IDicomDirectoryFinder
- {
- protected readonly ILogger Logger;
-
- protected readonly string FileSystemRoot;
- protected readonly IFileSystem FileSystem;
-
- private readonly IProducerModel _directoriesProducerModel;
- protected int TotalSent;
+ protected readonly ILogger Logger;
- protected bool IsProcessing;
- protected readonly CancellationTokenSource TokenSource = new();
+ protected readonly string FileSystemRoot;
+ protected readonly IFileSystem FileSystem;
- protected readonly Stopwatch Stopwatch = new();
- protected StringBuilder? StringBuilder;
- protected List>? Times;
+ private readonly IProducerModel _directoriesProducerModel;
+ protected int TotalSent;
- ///
- /// The filenames to look for in directories. Defaults to *.dcm
- ///
- protected readonly string SearchPattern;
-
- protected enum TimeLabel
- {
- NewDirInfo,
- EnumFiles,
- FirstOrDef,
- FoundNewDir,
- EnumDirs,
- PushDirs
- }
+ protected bool IsProcessing;
+ protected readonly CancellationTokenSource TokenSource = new();
+ protected readonly Stopwatch Stopwatch = new();
+ protected StringBuilder? StringBuilder;
+ protected List>? Times;
- protected DicomDirectoryFinder(
- string fileSystemRoot,
- IFileSystem fileSystem,
- string dicomSearchPattern,
- IProducerModel directoriesProducerModel
- )
- {
- FileSystemRoot = fileSystemRoot;
- FileSystem = fileSystem;
- SearchPattern = dicomSearchPattern;
- _directoriesProducerModel = directoriesProducerModel;
- Logger = LogManager.GetLogger(GetType().Name);
- }
+ ///
+ /// The filenames to look for in directories. Defaults to *.dcm
+ ///
+ protected readonly string SearchPattern;
- public abstract void SearchForDicomDirectories(string rootDir);
+ protected enum TimeLabel
+ {
+ NewDirInfo,
+ EnumFiles,
+ FirstOrDef,
+ FoundNewDir,
+ EnumDirs,
+ PushDirs
+ }
- public void Stop()
- {
- if (!IsProcessing)
- return;
- Logger.Info("Stop requested while still processing, attempting to kill");
- TokenSource.Cancel();
+ protected DicomDirectoryFinder(
+ string fileSystemRoot,
+ IFileSystem fileSystem,
+ string dicomSearchPattern,
+ IProducerModel directoriesProducerModel
+ )
+ {
+ FileSystemRoot = fileSystemRoot;
+ FileSystem = fileSystem;
+ SearchPattern = dicomSearchPattern;
+ _directoriesProducerModel = directoriesProducerModel;
+ Logger = LogManager.GetLogger(GetType().Name);
+ }
- var timeout = 5000;
- const int delta = 500;
- while (IsProcessing && timeout > 0)
- {
- Thread.Sleep(delta);
- timeout -= delta;
- }
+ public abstract void SearchForDicomDirectories(string rootDir);
- if (timeout <= 0)
- throw new ApplicationException("SearchForDicomDirectories did not exit in time");
+ public void Stop()
+ {
+ if (!IsProcessing)
+ return;
- Logger.Info("Directory scan aborted, exiting");
- }
+ Logger.Info("Stop requested while still processing, attempting to kill");
+ TokenSource.Cancel();
- ///
- /// Handled when a new DICOM directory is found. Writes an AccessionDirectoryMessage to the message exchange
- ///
- /// Full path to a directory that has been found to contain a DICOM file
- protected void FoundNewDicomDirectory(string dir)
+ var timeout = 5000;
+ const int delta = 500;
+ while (IsProcessing && timeout > 0)
{
- Logger.Debug("DicomDirectoryFinder: Found " + dir);
+ Thread.Sleep(delta);
+ timeout -= delta;
+ }
- string dirPath = Path.GetFullPath(dir).TrimEnd(Path.DirectorySeparatorChar);
+ if (timeout <= 0)
+ throw new ApplicationException("SearchForDicomDirectories did not exit in time");
- if (dirPath.StartsWith(FileSystemRoot))
- dirPath = dirPath.Remove(0, FileSystemRoot.Length);
+ Logger.Info("Directory scan aborted, exiting");
+ }
- dirPath = dirPath.TrimStart(Path.DirectorySeparatorChar);
+ ///
+ /// Handled when a new DICOM directory is found. Writes an AccessionDirectoryMessage to the message exchange
+ ///
+ /// Full path to a directory that has been found to contain a DICOM file
+ protected void FoundNewDicomDirectory(string dir)
+ {
+ Logger.Debug("DicomDirectoryFinder: Found " + dir);
- var message = new AccessionDirectoryMessage
- {
- DirectoryPath = dirPath,
- };
+ string dirPath = Path.GetFullPath(dir).TrimEnd(Path.DirectorySeparatorChar);
- _directoriesProducerModel.SendMessage(message, isInResponseTo: null, routingKey: null);
- ++TotalSent;
- }
+ if (dirPath.StartsWith(FileSystemRoot))
+ dirPath = dirPath.Remove(0, FileSystemRoot.Length);
- protected void LogTime(TimeLabel tl)
- {
- long elapsed = Stopwatch.ElapsedMilliseconds;
- StringBuilder!.Append(tl + "=" + elapsed + "ms ");
- Times![(int)tl].Add(elapsed);
- Stopwatch.Restart();
- }
+ dirPath = dirPath.TrimStart(Path.DirectorySeparatorChar);
- protected string CalcAverages()
+ var message = new AccessionDirectoryMessage
{
- var sb = new StringBuilder();
- sb.AppendLine("Averages:");
+ DirectoryPath = dirPath,
+ };
- foreach (TimeLabel label in (TimeLabel[])Enum.GetValues(typeof(TimeLabel)))
- {
- int count = Times![(int)label].Count;
- long average = count == 0 ? 0 : Times[(int)label].Sum() / count;
+ _directoriesProducerModel.SendMessage(message, isInResponseTo: null, routingKey: null);
+ ++TotalSent;
+ }
- sb.AppendLine(label + ":\t" + average + "ms");
- }
+ protected void LogTime(TimeLabel tl)
+ {
+ long elapsed = Stopwatch.ElapsedMilliseconds;
+ StringBuilder!.Append(tl + "=" + elapsed + "ms ");
+ Times![(int)tl].Add(elapsed);
+ Stopwatch.Restart();
+ }
- return sb.ToString();
- }
+ protected string CalcAverages()
+ {
+ var sb = new StringBuilder();
+ sb.AppendLine("Averages:");
- protected virtual IEnumerable GetEnumerator(IDirectoryInfo dirInfo)
+ foreach (TimeLabel label in (TimeLabel[])Enum.GetValues(typeof(TimeLabel)))
{
- return dirInfo.EnumerateFiles(SearchPattern);
+ int count = Times![(int)label].Count;
+ long average = count == 0 ? 0 : Times[(int)label].Sum() / count;
+
+ sb.AppendLine(label + ":\t" + average + "ms");
}
+
+ return sb.ToString();
+ }
+
+ protected virtual IEnumerable GetEnumerator(IDirectoryInfo dirInfo)
+ {
+ return dirInfo.EnumerateFiles(SearchPattern);
}
}
diff --git a/src/SmiServices/Applications/DicomDirectoryProcessor/DirectoryFinders/IDicomDirectoryFinder.cs b/src/SmiServices/Applications/DicomDirectoryProcessor/DirectoryFinders/IDicomDirectoryFinder.cs
index 6d9301df0..04739647d 100644
--- a/src/SmiServices/Applications/DicomDirectoryProcessor/DirectoryFinders/IDicomDirectoryFinder.cs
+++ b/src/SmiServices/Applications/DicomDirectoryProcessor/DirectoryFinders/IDicomDirectoryFinder.cs
@@ -1,21 +1,20 @@
using SmiServices.Common.Messages;
-namespace SmiServices.Applications.DicomDirectoryProcessor.DirectoryFinders
+namespace SmiServices.Applications.DicomDirectoryProcessor.DirectoryFinders;
+
+///
+/// Interface for classes which scan a directory for dicom files
+///
+public interface IDicomDirectoryFinder
{
///
- /// Interface for classes which scan a directory for dicom files
+ /// Performs the directory scan, sending s where it finds dicom files
///
- public interface IDicomDirectoryFinder
- {
- ///
- /// Performs the directory scan, sending s where it finds dicom files
- ///
- /// The full path to start the scan at
- void SearchForDicomDirectories(string rootDir);
+ /// The full path to start the scan at
+ void SearchForDicomDirectories(string rootDir);
- ///
- /// Stops the scan if it is still running. Implementations must ensure they exit promptly when requested.
- ///
- void Stop();
- }
+ ///
+ /// Stops the scan if it is still running. Implementations must ensure they exit promptly when requested.
+ ///
+ void Stop();
}
diff --git a/src/SmiServices/Applications/DicomDirectoryProcessor/DirectoryFinders/PacsDirectoryFinder.cs b/src/SmiServices/Applications/DicomDirectoryProcessor/DirectoryFinders/PacsDirectoryFinder.cs
index ff25abba6..f720e2b0d 100644
--- a/src/SmiServices/Applications/DicomDirectoryProcessor/DirectoryFinders/PacsDirectoryFinder.cs
+++ b/src/SmiServices/Applications/DicomDirectoryProcessor/DirectoryFinders/PacsDirectoryFinder.cs
@@ -5,81 +5,80 @@
using System.Linq;
using System.Text.RegularExpressions;
-namespace SmiServices.Applications.DicomDirectoryProcessor.DirectoryFinders
+namespace SmiServices.Applications.DicomDirectoryProcessor.DirectoryFinders;
+
+public class PacsDirectoryFinder : DicomDirectoryFinder
{
- public class PacsDirectoryFinder : DicomDirectoryFinder
- {
- // Regex that matches when we are at the yyyy\mm\dd\ directory level
- private readonly Regex _dayDirectoryRegex = new(@"(20\d{2}[\\\/]\d{2}[\\\/]\d{2})([\\\/]|$)");
- // Regex that matches when we are at the yyyy\mm\dd\xxxxx directory level
- private readonly Regex _accDirectoryRegex = new(@"(20\d{2}[\\\/]\d{2}[\\\/]\d{2}[\\\/][a-zA-Z0-9._-]+[\\\/]$)");
+ // Regex that matches when we are at the yyyy\mm\dd\ directory level
+ private readonly Regex _dayDirectoryRegex = new(@"(20\d{2}[\\\/]\d{2}[\\\/]\d{2})([\\\/]|$)");
+ // Regex that matches when we are at the yyyy\mm\dd\xxxxx directory level
+ private readonly Regex _accDirectoryRegex = new(@"(20\d{2}[\\\/]\d{2}[\\\/]\d{2}[\\\/][a-zA-Z0-9._-]+[\\\/]$)");
+
+ public PacsDirectoryFinder(string fileSystemRoot, IFileSystem fileSystem, string dicomSearchPattern, IProducerModel directoriesProducerModel)
+ : base(fileSystemRoot, fileSystem, dicomSearchPattern, directoriesProducerModel) { }
- public PacsDirectoryFinder(string fileSystemRoot, IFileSystem fileSystem, string dicomSearchPattern, IProducerModel directoriesProducerModel)
- : base(fileSystemRoot, fileSystem, dicomSearchPattern, directoriesProducerModel) { }
+ public PacsDirectoryFinder(string fileSystemRoot, string dicomSearchPattern, IProducerModel directoriesProducerModel)
+ : base(fileSystemRoot, new FileSystem(), dicomSearchPattern, directoriesProducerModel) { }
- public PacsDirectoryFinder(string fileSystemRoot, string dicomSearchPattern, IProducerModel directoriesProducerModel)
- : base(fileSystemRoot, new FileSystem(), dicomSearchPattern, directoriesProducerModel) { }
+ public override void SearchForDicomDirectories(string rootDir)
+ {
+ Logger.Info("Starting directory scan of: " + rootDir);
+ IsProcessing = true;
+ TotalSent = 0;
+
+ if (!FileSystem.Directory.Exists(rootDir))
+ throw new DirectoryNotFoundException("Could not find the root directory at the start of the scan \"" + rootDir + "\"");
- public override void SearchForDicomDirectories(string rootDir)
+ // Check if we were given an accession directory
+ if (_accDirectoryRegex.IsMatch(rootDir))
{
- Logger.Info("Starting directory scan of: " + rootDir);
- IsProcessing = true;
- TotalSent = 0;
+ Logger.Debug("Given an accession directory, sending single message");
+ FoundNewDicomDirectory(rootDir.Remove(0, FileSystemRoot.Length));
+ }
+ else
+ {
+ Times = [];
+ for (var i = 0; i < 6; ++i)
+ Times.Add([]);
- if (!FileSystem.Directory.Exists(rootDir))
- throw new DirectoryNotFoundException("Could not find the root directory at the start of the scan \"" + rootDir + "\"");
+ var dirStack = new Stack();
+ dirStack.Push(rootDir);
- // Check if we were given an accession directory
- if (_accDirectoryRegex.IsMatch(rootDir))
- {
- Logger.Debug("Given an accession directory, sending single message");
- FoundNewDicomDirectory(rootDir.Remove(0, FileSystemRoot.Length));
- }
- else
+ while (dirStack.Count > 0 && !TokenSource.IsCancellationRequested)
{
- Times = [];
- for (var i = 0; i < 6; ++i)
- Times.Add([]);
+ string dir = dirStack.Pop();
+ Logger.Debug("Scanning " + dir);
- var dirStack = new Stack();
- dirStack.Push(rootDir);
+ IDirectoryInfo dirInfo = FileSystem.DirectoryInfo.New(dir);
- while (dirStack.Count > 0 && !TokenSource.IsCancellationRequested)
+ if (!dirInfo.Exists)
{
- string dir = dirStack.Pop();
- Logger.Debug("Scanning " + dir);
-
- IDirectoryInfo dirInfo = FileSystem.DirectoryInfo.New(dir);
-
- if (!dirInfo.Exists)
- {
- Logger.Warn("Can no longer find " + dir + ", continuing");
- continue;
- }
-
- IEnumerable subDirs = dirInfo.EnumerateDirectories();
-
- if (_dayDirectoryRegex.IsMatch(dir))
- {
- Logger.Debug("At the day level, assuming all subdirs are accession directories");
- // At the day level, so each of the subdirectories will be accession directories
- foreach (IDirectoryInfo accessionDir in subDirs)
- FoundNewDicomDirectory(accessionDir.FullName);
- }
- else
- {
- Logger.Debug("Not at the day level, checking subdirectories");
- subDirs.ToList().ForEach(x => dirStack.Push(x.FullName));
- }
+ Logger.Warn("Can no longer find " + dir + ", continuing");
+ continue;
}
- }
- IsProcessing = false;
+ IEnumerable subDirs = dirInfo.EnumerateDirectories();
- Logger.Info("Directory scan finished");
- Logger.Info("Total messages sent: " + TotalSent);
+ if (_dayDirectoryRegex.IsMatch(dir))
+ {
+ Logger.Debug("At the day level, assuming all subdirs are accession directories");
+ // At the day level, so each of the subdirectories will be accession directories
+ foreach (IDirectoryInfo accessionDir in subDirs)
+ FoundNewDicomDirectory(accessionDir.FullName);
+ }
+ else
+ {
+ Logger.Debug("Not at the day level, checking subdirectories");
+ subDirs.ToList().ForEach(x => dirStack.Push(x.FullName));
+ }
+ }
}
+
+ IsProcessing = false;
+
+ Logger.Info("Directory scan finished");
+ Logger.Info("Total messages sent: " + TotalSent);
}
}
diff --git a/src/SmiServices/Applications/DicomDirectoryProcessor/DirectoryFinders/ZipDicomDirectoryFinder.cs b/src/SmiServices/Applications/DicomDirectoryProcessor/DirectoryFinders/ZipDicomDirectoryFinder.cs
index 4cf0f2703..f3aa5bc5f 100644
--- a/src/SmiServices/Applications/DicomDirectoryProcessor/DirectoryFinders/ZipDicomDirectoryFinder.cs
+++ b/src/SmiServices/Applications/DicomDirectoryProcessor/DirectoryFinders/ZipDicomDirectoryFinder.cs
@@ -4,27 +4,26 @@
using System.IO.Abstractions;
using System.Linq;
-namespace SmiServices.Applications.DicomDirectoryProcessor.DirectoryFinders
+namespace SmiServices.Applications.DicomDirectoryProcessor.DirectoryFinders;
+
+///
+/// Finds directories that contain zip files or dicom files. Does not require files to be in a directory structure
+/// that contains AccessionNumber
+///
+public class ZipDicomDirectoryFinder : BasicDicomDirectoryFinder
{
- ///
- /// Finds directories that contain zip files or dicom files. Does not require files to be in a directory structure
- /// that contains AccessionNumber
- ///
- public class ZipDicomDirectoryFinder : BasicDicomDirectoryFinder
+ public ZipDicomDirectoryFinder(string fileSystemRoot, IFileSystem fileSystem, string dicomSearchPattern, IProducerModel directoriesProducerModel)
+ : base(fileSystemRoot, fileSystem, dicomSearchPattern, directoriesProducerModel)
{
- public ZipDicomDirectoryFinder(string fileSystemRoot, IFileSystem fileSystem, string dicomSearchPattern, IProducerModel directoriesProducerModel)
- : base(fileSystemRoot, fileSystem, dicomSearchPattern, directoriesProducerModel)
- {
- AlwaysSearchSubdirectories = true;
- }
+ AlwaysSearchSubdirectories = true;
+ }
- public ZipDicomDirectoryFinder(string fileSystemRoot, string dicomSearchPattern, IProducerModel directoriesProducerModel)
- : this(fileSystemRoot, new FileSystem(), dicomSearchPattern, directoriesProducerModel) { }
+ public ZipDicomDirectoryFinder(string fileSystemRoot, string dicomSearchPattern, IProducerModel directoriesProducerModel)
+ : this(fileSystemRoot, new FileSystem(), dicomSearchPattern, directoriesProducerModel) { }
- protected override IEnumerable GetEnumerator(IDirectoryInfo dirInfo)
- {
- return dirInfo.EnumerateFiles().Where(f => f.Extension == ".dcm" || ZipHelper.IsZip(f));
- }
+ protected override IEnumerable GetEnumerator(IDirectoryInfo dirInfo)
+ {
+ return dirInfo.EnumerateFiles().Where(f => f.Extension == ".dcm" || ZipHelper.IsZip(f));
}
}
diff --git a/src/SmiServices/Applications/ExtractImages/CohortCsvParser.cs b/src/SmiServices/Applications/ExtractImages/CohortCsvParser.cs
index 07d90710a..9e3ee444b 100644
--- a/src/SmiServices/Applications/ExtractImages/CohortCsvParser.cs
+++ b/src/SmiServices/Applications/ExtractImages/CohortCsvParser.cs
@@ -9,57 +9,56 @@
using System.Linq;
-namespace SmiServices.Applications.ExtractImages
+namespace SmiServices.Applications.ExtractImages;
+
+public class CohortCsvParser
{
- public class CohortCsvParser
+ // NOTE(rkm 2021-04-01) Just do a simple line-by-line read through the CSV
+ private static readonly CsvConfiguration _csvConfiguration = new(CultureInfo.InvariantCulture)
{
- // NOTE(rkm 2021-04-01) Just do a simple line-by-line read through the CSV
- private static readonly CsvConfiguration _csvConfiguration = new(CultureInfo.InvariantCulture)
- {
- HasHeaderRecord = false
- };
+ HasHeaderRecord = false
+ };
- private readonly IFileSystem _fileSystem;
+ private readonly IFileSystem _fileSystem;
- public CohortCsvParser(IFileSystem fileSystem)
- {
- _fileSystem = fileSystem;
- }
+ public CohortCsvParser(IFileSystem fileSystem)
+ {
+ _fileSystem = fileSystem;
+ }
- public Tuple> Parse(string csvFilePath)
- {
- using var fileStream = _fileSystem.FileStream.New(csvFilePath, FileMode.Open, FileAccess.Read);
- using var streamReader = new StreamReader(fileStream);
- using var reader = new CsvReader(streamReader, _csvConfiguration);
+ public Tuple> Parse(string csvFilePath)
+ {
+ using var fileStream = _fileSystem.FileStream.New(csvFilePath, FileMode.Open, FileAccess.Read);
+ using var streamReader = new StreamReader(fileStream);
+ using var reader = new CsvReader(streamReader, _csvConfiguration);
- reader.Read();
- var headerRecord = reader.Parser.Record ?? throw new ApplicationException(message: "CSV is empty");
+ reader.Read();
+ var headerRecord = reader.Parser.Record ?? throw new ApplicationException(message: "CSV is empty");
- if (headerRecord.Length != 1)
- throw new ApplicationException(message: "CSV must have exactly 1 column");
+ if (headerRecord.Length != 1)
+ throw new ApplicationException(message: "CSV must have exactly 1 column");
- if (!Enum.TryParse(headerRecord[0], out var extractionKey))
- {
- var keys = string.Join(separator: ',', Enum.GetNames(typeof(ExtractionKey)));
- throw new ApplicationException($"CSV header must be a valid ExtractionKey: {keys}");
- }
+ if (!Enum.TryParse(headerRecord[0], out var extractionKey))
+ {
+ var keys = string.Join(separator: ',', Enum.GetNames(typeof(ExtractionKey)));
+ throw new ApplicationException($"CSV header must be a valid ExtractionKey: {keys}");
+ }
- var allIds = new List();
- while (reader.Read())
- {
- var record = reader.Parser.Record;
- if (record.Length != 1)
- throw new ApplicationException(message: "CSV must have exactly 1 column");
+ var allIds = new List();
+ while (reader.Read())
+ {
+ var record = reader.Parser.Record;
+ if (record.Length != 1)
+ throw new ApplicationException(message: "CSV must have exactly 1 column");
- var id = record[0]?.Trim();
- if (!string.IsNullOrWhiteSpace(id))
- allIds.Add(id);
- }
+ var id = record[0]?.Trim();
+ if (!string.IsNullOrWhiteSpace(id))
+ allIds.Add(id);
+ }
- if (allIds.Count == 0)
- throw new ApplicationException(message: "No records in the cohort CSV");
+ if (allIds.Count == 0)
+ throw new ApplicationException(message: "No records in the cohort CSV");
- return new Tuple>(extractionKey, allIds);
- }
+ return new Tuple>(extractionKey, allIds);
}
}
diff --git a/src/SmiServices/Applications/ExtractImages/ExtractImages.cs b/src/SmiServices/Applications/ExtractImages/ExtractImages.cs
index f19fbf9bb..e7c355499 100644
--- a/src/SmiServices/Applications/ExtractImages/ExtractImages.cs
+++ b/src/SmiServices/Applications/ExtractImages/ExtractImages.cs
@@ -4,28 +4,27 @@
using System.Diagnostics.CodeAnalysis;
-namespace SmiServices.Applications.ExtractImages
+namespace SmiServices.Applications.ExtractImages;
+
+public static class ExtractImages
{
- public static class ExtractImages
+ [ExcludeFromCodeCoverage]
+ public static int Main(IEnumerable args)
{
- [ExcludeFromCodeCoverage]
- public static int Main(IEnumerable args)
- {
- int ret = SmiCliInit
- .ParseAndRun(
- args,
- nameof(ExtractImages),
- OnParse
- );
- return ret;
- }
+ int ret = SmiCliInit
+ .ParseAndRun(
+ args,
+ nameof(ExtractImages),
+ OnParse
+ );
+ return ret;
+ }
- private static int OnParse(GlobalOptions globals, ExtractImagesCliOptions parsedOptions)
- {
- var bootstrapper =
- new MicroserviceHostBootstrapper(() => new ExtractImagesHost(globals, parsedOptions));
- int ret = bootstrapper.Main();
- return ret;
- }
+ private static int OnParse(GlobalOptions globals, ExtractImagesCliOptions parsedOptions)
+ {
+ var bootstrapper =
+ new MicroserviceHostBootstrapper(() => new ExtractImagesHost(globals, parsedOptions));
+ int ret = bootstrapper.Main();
+ return ret;
}
}
diff --git a/src/SmiServices/Applications/ExtractImages/ExtractImagesCliOptions.cs b/src/SmiServices/Applications/ExtractImages/ExtractImagesCliOptions.cs
index 1db2d78f8..cbbb05648 100644
--- a/src/SmiServices/Applications/ExtractImages/ExtractImagesCliOptions.cs
+++ b/src/SmiServices/Applications/ExtractImages/ExtractImagesCliOptions.cs
@@ -6,76 +6,75 @@
using System.Text;
-namespace SmiServices.Applications.ExtractImages
+namespace SmiServices.Applications.ExtractImages;
+
+public class ExtractImagesCliOptions : CliOptions
{
- public class ExtractImagesCliOptions : CliOptions
- {
- // Required
+ // Required
- [Option(shortName: 'p', longName: "project-id", Required = true, HelpText = "The project identifier")]
- public string ProjectId { get; set; } = null!;
+ [Option(shortName: 'p', longName: "project-id", Required = true, HelpText = "The project identifier")]
+ public string ProjectId { get; set; } = null!;
- [Option(shortName: 'c', longName: "cohort-csv-file", Required = true,
- HelpText = "The CSV file containing IDs of the cohort for extraction")]
- public string CohortCsvFile { get; set; } = null!;
+ [Option(shortName: 'c', longName: "cohort-csv-file", Required = true,
+ HelpText = "The CSV file containing IDs of the cohort for extraction")]
+ public string CohortCsvFile { get; set; } = null!;
- [Option(shortName: 'm', longName: "modality", Required = true,
- HelpText = "The modality to extract. Any non-matching IDs from the input list are ignored")]
- public string Modality { get; set; } = null!;
+ [Option(shortName: 'm', longName: "modality", Required = true,
+ HelpText = "The modality to extract. Any non-matching IDs from the input list are ignored")]
+ public string Modality { get; set; } = null!;
- // Optional
+ // Optional
- [Option(shortName: 'i', longName: "identifiable-extraction", Required = false,
- HelpText = "Extract without performing anonymisation")]
- public bool IsIdentifiableExtraction { get; set; }
+ [Option(shortName: 'i', longName: "identifiable-extraction", Required = false,
+ HelpText = "Extract without performing anonymisation")]
+ public bool IsIdentifiableExtraction { get; set; }
- [Option(shortName: 'f', longName: "no-filters", Required = false,
- HelpText = "Extract without applying any rejection filters")]
- public bool IsNoFiltersExtraction { get; set; }
+ [Option(shortName: 'f', longName: "no-filters", Required = false,
+ HelpText = "Extract without applying any rejection filters")]
+ public bool IsNoFiltersExtraction { get; set; }
- [Option(shortName: 'n', longName: "non-interactive", Required = false,
- HelpText = "Don't pause for manual confirmation before sending messages")]
- public bool NonInteractive { get; set; }
+ [Option(shortName: 'n', longName: "non-interactive", Required = false,
+ HelpText = "Don't pause for manual confirmation before sending messages")]
+ public bool NonInteractive { get; set; }
- [Option(shortName: 'u', longName: "pooled-extraction", Required = false, Default = false,
- HelpText = "True to use the global file pool for this extraction")]
- public bool IsPooledExtraction { get; set; }
+ [Option(shortName: 'u', longName: "pooled-extraction", Required = false, Default = false,
+ HelpText = "True to use the global file pool for this extraction")]
+ public bool IsPooledExtraction { get; set; }
- [Usage]
- [ExcludeFromCodeCoverage]
- public static IEnumerable Examples
+ [Usage]
+ [ExcludeFromCodeCoverage]
+ public static IEnumerable Examples
+ {
+ get
{
- get
- {
- yield return new Example(helpText: "Normal Scenario",
- new ExtractImagesCliOptions { CohortCsvFile = "my.csv", ProjectId = "1234-5678" });
- yield return new Example(helpText: "Extract CTs without anonymisation",
- new ExtractImagesCliOptions
- {
- CohortCsvFile = "my.csv",
- ProjectId = "1234-5678",
- Modality = "CT",
- IsIdentifiableExtraction = true
- });
- yield return new Example(helpText: "Extract without applying any rejection filters",
- new ExtractImagesCliOptions
- { CohortCsvFile = "my.csv", ProjectId = "1234-5678", IsNoFiltersExtraction = true });
- }
+ yield return new Example(helpText: "Normal Scenario",
+ new ExtractImagesCliOptions { CohortCsvFile = "my.csv", ProjectId = "1234-5678" });
+ yield return new Example(helpText: "Extract CTs without anonymisation",
+ new ExtractImagesCliOptions
+ {
+ CohortCsvFile = "my.csv",
+ ProjectId = "1234-5678",
+ Modality = "CT",
+ IsIdentifiableExtraction = true
+ });
+ yield return new Example(helpText: "Extract without applying any rejection filters",
+ new ExtractImagesCliOptions
+ { CohortCsvFile = "my.csv", ProjectId = "1234-5678", IsNoFiltersExtraction = true });
}
+ }
- [ExcludeFromCodeCoverage]
- public override string ToString()
- {
- var sb = new StringBuilder();
- sb.Append(base.ToString());
- sb.Append($"ProjectId={ProjectId},");
- sb.Append($"CohortCsvFile={CohortCsvFile},");
- sb.Append($"Modality={Modality},");
- sb.Append($"IdentifiableExtraction={IsIdentifiableExtraction},");
- sb.Append($"NoFiltersExtraction={IsNoFiltersExtraction},");
- sb.Append($"NonInteractive={NonInteractive},");
- return sb.ToString();
- }
+ [ExcludeFromCodeCoverage]
+ public override string ToString()
+ {
+ var sb = new StringBuilder();
+ sb.Append(base.ToString());
+ sb.Append($"ProjectId={ProjectId},");
+ sb.Append($"CohortCsvFile={CohortCsvFile},");
+ sb.Append($"Modality={Modality},");
+ sb.Append($"IdentifiableExtraction={IsIdentifiableExtraction},");
+ sb.Append($"NoFiltersExtraction={IsNoFiltersExtraction},");
+ sb.Append($"NonInteractive={NonInteractive},");
+ return sb.ToString();
}
}
diff --git a/src/SmiServices/Applications/ExtractImages/ExtractImagesHost.cs b/src/SmiServices/Applications/ExtractImages/ExtractImagesHost.cs
index 82f90ac2d..fd5062813 100644
--- a/src/SmiServices/Applications/ExtractImages/ExtractImagesHost.cs
+++ b/src/SmiServices/Applications/ExtractImages/ExtractImagesHost.cs
@@ -11,103 +11,102 @@
using System.Linq;
-namespace SmiServices.Applications.ExtractImages
+namespace SmiServices.Applications.ExtractImages;
+
+public class ExtractImagesHost : MicroserviceHost
{
- public class ExtractImagesHost : MicroserviceHost
- {
- private readonly IFileSystem _fileSystem;
+ private readonly IFileSystem _fileSystem;
+
+ private readonly string _csvFilePath;
- private readonly string _csvFilePath;
+ private readonly IExtractionMessageSender _extractionMessageSender;
- private readonly IExtractionMessageSender _extractionMessageSender;
+ private readonly string _absoluteExtractionDir;
- private readonly string _absoluteExtractionDir;
+ private readonly ExtractionKey[]? _allowedKeys;
+
+ public ExtractImagesHost(
+ GlobalOptions globals,
+ ExtractImagesCliOptions cliOptions,
+ IExtractionMessageSender? extractionMessageSender = null,
+ IMessageBroker? messageBroker = null,
+ IFileSystem? fileSystem = null,
+ bool threaded = false
+ )
+ : base(
+ globals,
+ messageBroker)
+ {
+ ExtractImagesOptions? options = Globals.ExtractImagesOptions ?? throw new ArgumentException(nameof(Globals.ExtractImagesOptions));
+ _allowedKeys = options.AllowedExtractionKeys;
- private readonly ExtractionKey[]? _allowedKeys;
+ _fileSystem = fileSystem ?? new FileSystem();
- public ExtractImagesHost(
- GlobalOptions globals,
- ExtractImagesCliOptions cliOptions,
- IExtractionMessageSender? extractionMessageSender = null,
- IMessageBroker? messageBroker = null,
- IFileSystem? fileSystem = null,
- bool threaded = false
- )
- : base(
- globals,
- messageBroker)
+ string extractRoot = Globals.FileSystemOptions?.ExtractRoot ?? throw new ArgumentException("Some part of Globals.FileSystemOptions.ExtractRoot was null");
+ if (!_fileSystem.Directory.Exists(extractRoot))
+ throw new DirectoryNotFoundException($"Could not find the extraction root '{extractRoot}'");
+
+ if (cliOptions.IsPooledExtraction)
{
- ExtractImagesOptions? options = Globals.ExtractImagesOptions ?? throw new ArgumentException(nameof(Globals.ExtractImagesOptions));
- _allowedKeys = options.AllowedExtractionKeys;
-
- _fileSystem = fileSystem ?? new FileSystem();
-
- string extractRoot = Globals.FileSystemOptions?.ExtractRoot ?? throw new ArgumentException("Some part of Globals.FileSystemOptions.ExtractRoot was null");
- if (!_fileSystem.Directory.Exists(extractRoot))
- throw new DirectoryNotFoundException($"Could not find the extraction root '{extractRoot}'");
-
- if (cliOptions.IsPooledExtraction)
- {
- if (!_fileSystem.Directory.Exists(Globals.FileSystemOptions.ExtractionPoolRoot))
- throw new InvalidOperationException($"{nameof(cliOptions.IsPooledExtraction)} can only be passed if {nameof(Globals.FileSystemOptions.ExtractionPoolRoot)} is a directory");
-
- if (cliOptions.IsIdentifiableExtraction)
- throw new InvalidOperationException($"{nameof(cliOptions.IsPooledExtraction)} is incompatible with {nameof(cliOptions.IsIdentifiableExtraction)}");
-
- if (cliOptions.IsNoFiltersExtraction)
- throw new InvalidOperationException($"{nameof(cliOptions.IsPooledExtraction)} is incompatible with {nameof(cliOptions.IsNoFiltersExtraction)}");
- }
-
- _csvFilePath = cliOptions.CohortCsvFile;
- if (string.IsNullOrWhiteSpace(_csvFilePath))
- throw new ArgumentNullException(nameof(cliOptions.CohortCsvFile));
- if (!_fileSystem.File.Exists(_csvFilePath))
- throw new FileNotFoundException($"Could not find the cohort CSV file '{_csvFilePath}'");
-
- // TODO(rkm 2021-04-01) Now that all the extraction path code is in C#, we would benefit from refactoring it all out
- // to a helper class to support having multiple configurations (and probably prevent some bugs)
- string extractionName = _fileSystem.Path.GetFileNameWithoutExtension(_csvFilePath);
- string extractionDir = _fileSystem.Path.Join(cliOptions.ProjectId, "extractions", extractionName);
- _absoluteExtractionDir = _fileSystem.Path.Join(extractRoot, extractionDir);
-
- if (_fileSystem.Directory.Exists(_absoluteExtractionDir))
- throw new DirectoryNotFoundException($"Extraction directory already exists '{_absoluteExtractionDir}'");
-
- if (extractionMessageSender == null)
- {
- IProducerModel extractionRequestProducer = MessageBroker.SetupProducer(options.ExtractionRequestProducerOptions!, isBatch: false);
- IProducerModel extractionRequestInfoProducer = MessageBroker.SetupProducer(options.ExtractionRequestInfoProducerOptions!, isBatch: false);
-
- _extractionMessageSender = new ExtractionMessageSender(
- options,
- cliOptions,
- extractionRequestProducer,
- extractionRequestInfoProducer,
- _fileSystem,
- extractRoot,
- extractionDir,
- new DateTimeProvider(),
- new RealConsoleInput()
- );
- }
- else
- {
- Logger.Warn($"{nameof(Globals.ExtractImagesOptions.MaxIdentifiersPerMessage)} will be ignored here");
- _extractionMessageSender = extractionMessageSender;
- }
+ if (!_fileSystem.Directory.Exists(Globals.FileSystemOptions.ExtractionPoolRoot))
+ throw new InvalidOperationException($"{nameof(cliOptions.IsPooledExtraction)} can only be passed if {nameof(Globals.FileSystemOptions.ExtractionPoolRoot)} is a directory");
+
+ if (cliOptions.IsIdentifiableExtraction)
+ throw new InvalidOperationException($"{nameof(cliOptions.IsPooledExtraction)} is incompatible with {nameof(cliOptions.IsIdentifiableExtraction)}");
+
+ if (cliOptions.IsNoFiltersExtraction)
+ throw new InvalidOperationException($"{nameof(cliOptions.IsPooledExtraction)} is incompatible with {nameof(cliOptions.IsNoFiltersExtraction)}");
}
- public override void Start()
- {
- var parser = new CohortCsvParser(_fileSystem);
- (ExtractionKey extractionKey, List idList) = parser.Parse(_csvFilePath);
+ _csvFilePath = cliOptions.CohortCsvFile;
+ if (string.IsNullOrWhiteSpace(_csvFilePath))
+ throw new ArgumentNullException(nameof(cliOptions.CohortCsvFile));
+ if (!_fileSystem.File.Exists(_csvFilePath))
+ throw new FileNotFoundException($"Could not find the cohort CSV file '{_csvFilePath}'");
- if (_allowedKeys?.Contains(extractionKey) == false)
- throw new InvalidOperationException($"'{extractionKey}' from CSV not in list of supported extraction keys ({string.Join(',', _allowedKeys)})");
+ // TODO(rkm 2021-04-01) Now that all the extraction path code is in C#, we would benefit from refactoring it all out
+ // to a helper class to support having multiple configurations (and probably prevent some bugs)
+ string extractionName = _fileSystem.Path.GetFileNameWithoutExtension(_csvFilePath);
+ string extractionDir = _fileSystem.Path.Join(cliOptions.ProjectId, "extractions", extractionName);
+ _absoluteExtractionDir = _fileSystem.Path.Join(extractRoot, extractionDir);
- _extractionMessageSender.SendMessages(extractionKey, idList);
+ if (_fileSystem.Directory.Exists(_absoluteExtractionDir))
+ throw new DirectoryNotFoundException($"Extraction directory already exists '{_absoluteExtractionDir}'");
- Stop("Completed");
+ if (extractionMessageSender == null)
+ {
+ IProducerModel extractionRequestProducer = MessageBroker.SetupProducer(options.ExtractionRequestProducerOptions!, isBatch: false);
+ IProducerModel extractionRequestInfoProducer = MessageBroker.SetupProducer(options.ExtractionRequestInfoProducerOptions!, isBatch: false);
+
+ _extractionMessageSender = new ExtractionMessageSender(
+ options,
+ cliOptions,
+ extractionRequestProducer,
+ extractionRequestInfoProducer,
+ _fileSystem,
+ extractRoot,
+ extractionDir,
+ new DateTimeProvider(),
+ new RealConsoleInput()
+ );
+ }
+ else
+ {
+ Logger.Warn($"{nameof(Globals.ExtractImagesOptions.MaxIdentifiersPerMessage)} will be ignored here");
+ _extractionMessageSender = extractionMessageSender;
}
}
+
+ public override void Start()
+ {
+ var parser = new CohortCsvParser(_fileSystem);
+ (ExtractionKey extractionKey, List idList) = parser.Parse(_csvFilePath);
+
+ if (_allowedKeys?.Contains(extractionKey) == false)
+ throw new InvalidOperationException($"'{extractionKey}' from CSV not in list of supported extraction keys ({string.Join(',', _allowedKeys)})");
+
+ _extractionMessageSender.SendMessages(extractionKey, idList);
+
+ Stop("Completed");
+ }
}
diff --git a/src/SmiServices/Applications/ExtractImages/ExtractionMessageSender.cs b/src/SmiServices/Applications/ExtractImages/ExtractionMessageSender.cs
index 49b654a46..eaca477b3 100644
--- a/src/SmiServices/Applications/ExtractImages/ExtractionMessageSender.cs
+++ b/src/SmiServices/Applications/ExtractImages/ExtractionMessageSender.cs
@@ -9,189 +9,188 @@
using System.Linq;
using System.Text;
-namespace SmiServices.Applications.ExtractImages
+namespace SmiServices.Applications.ExtractImages;
+
+public class ExtractionMessageSender : IExtractionMessageSender
{
- public class ExtractionMessageSender : IExtractionMessageSender
+ private readonly ILogger _logger = LogManager.GetCurrentClassLogger();
+
+ private readonly IProducerModel _extractionRequestProducer;
+ private readonly IProducerModel _extractionRequestInfoProducer;
+ private readonly IFileSystem _fileSystem;
+ private readonly string _extractionRoot;
+ private readonly string _extractionDir;
+
+ private readonly DateTimeProvider _dateTimeProvider;
+ private readonly IConsoleInput _consoleInput;
+
+ private readonly int _maxIdentifiersPerMessage;
+
+ private readonly string _projectId;
+ private readonly string _modality;
+ private readonly bool _isIdentifiableExtraction;
+ private readonly bool _isNoFiltersExtraction;
+ private readonly bool _isPooledExtraction;
+ private readonly bool _nonInteractive;
+
+
+ public ExtractionMessageSender(
+ ExtractImagesOptions options,
+ ExtractImagesCliOptions cliOptions,
+ IProducerModel extractionRequestProducer,
+ IProducerModel extractionRequestInfoProducer,
+ IFileSystem fileSystem,
+ string extractionRoot,
+ string extractionDir,
+ DateTimeProvider dateTimeProvider,
+ IConsoleInput consoleInput
+ )
{
- private readonly ILogger _logger = LogManager.GetCurrentClassLogger();
-
- private readonly IProducerModel _extractionRequestProducer;
- private readonly IProducerModel _extractionRequestInfoProducer;
- private readonly IFileSystem _fileSystem;
- private readonly string _extractionRoot;
- private readonly string _extractionDir;
-
- private readonly DateTimeProvider _dateTimeProvider;
- private readonly IConsoleInput _consoleInput;
-
- private readonly int _maxIdentifiersPerMessage;
-
- private readonly string _projectId;
- private readonly string _modality;
- private readonly bool _isIdentifiableExtraction;
- private readonly bool _isNoFiltersExtraction;
- private readonly bool _isPooledExtraction;
- private readonly bool _nonInteractive;
-
-
- public ExtractionMessageSender(
- ExtractImagesOptions options,
- ExtractImagesCliOptions cliOptions,
- IProducerModel extractionRequestProducer,
- IProducerModel extractionRequestInfoProducer,
- IFileSystem fileSystem,
- string extractionRoot,
- string extractionDir,
- DateTimeProvider dateTimeProvider,
- IConsoleInput consoleInput
- )
- {
- _extractionRequestProducer = extractionRequestProducer;
- _extractionRequestInfoProducer = extractionRequestInfoProducer;
-
- _fileSystem = fileSystem;
- _extractionRoot = (!string.IsNullOrWhiteSpace(extractionRoot)) ? extractionRoot : throw new ArgumentOutOfRangeException(nameof(extractionRoot));
- _extractionDir = (!string.IsNullOrWhiteSpace(extractionDir)) ? extractionDir : throw new ArgumentOutOfRangeException(nameof(extractionDir));
- _dateTimeProvider = dateTimeProvider;
- _consoleInput = consoleInput;
-
- _maxIdentifiersPerMessage = options.MaxIdentifiersPerMessage;
- if (_maxIdentifiersPerMessage <= 0)
- throw new ArgumentOutOfRangeException(nameof(options));
-
- _projectId = (!string.IsNullOrWhiteSpace(cliOptions.ProjectId)) ? cliOptions.ProjectId : throw new ArgumentOutOfRangeException(nameof(cliOptions));
- _modality = (!string.IsNullOrWhiteSpace(cliOptions.Modality)) ? cliOptions.Modality : throw new ArgumentOutOfRangeException(nameof(cliOptions));
- _isIdentifiableExtraction = cliOptions.IsIdentifiableExtraction;
- _isNoFiltersExtraction = cliOptions.IsNoFiltersExtraction;
- _isPooledExtraction = cliOptions.IsPooledExtraction;
- _nonInteractive = cliOptions.NonInteractive;
- }
+ _extractionRequestProducer = extractionRequestProducer;
+ _extractionRequestInfoProducer = extractionRequestInfoProducer;
+
+ _fileSystem = fileSystem;
+ _extractionRoot = (!string.IsNullOrWhiteSpace(extractionRoot)) ? extractionRoot : throw new ArgumentOutOfRangeException(nameof(extractionRoot));
+ _extractionDir = (!string.IsNullOrWhiteSpace(extractionDir)) ? extractionDir : throw new ArgumentOutOfRangeException(nameof(extractionDir));
+ _dateTimeProvider = dateTimeProvider;
+ _consoleInput = consoleInput;
+
+ _maxIdentifiersPerMessage = options.MaxIdentifiersPerMessage;
+ if (_maxIdentifiersPerMessage <= 0)
+ throw new ArgumentOutOfRangeException(nameof(options));
+
+ _projectId = (!string.IsNullOrWhiteSpace(cliOptions.ProjectId)) ? cliOptions.ProjectId : throw new ArgumentOutOfRangeException(nameof(cliOptions));
+ _modality = (!string.IsNullOrWhiteSpace(cliOptions.Modality)) ? cliOptions.Modality : throw new ArgumentOutOfRangeException(nameof(cliOptions));
+ _isIdentifiableExtraction = cliOptions.IsIdentifiableExtraction;
+ _isNoFiltersExtraction = cliOptions.IsNoFiltersExtraction;
+ _isPooledExtraction = cliOptions.IsPooledExtraction;
+ _nonInteractive = cliOptions.NonInteractive;
+ }
- public void SendMessages(ExtractionKey extractionKey, List idList)
- {
- if (idList.Count == 0)
- throw new ArgumentException("ID list is empty");
+ public void SendMessages(ExtractionKey extractionKey, List idList)
+ {
+ if (idList.Count == 0)
+ throw new ArgumentException("ID list is empty");
- var jobId = Guid.NewGuid();
- DateTime now = _dateTimeProvider.UtcNow();
+ var jobId = Guid.NewGuid();
+ DateTime now = _dateTimeProvider.UtcNow();
- string userName = Environment.UserName;
+ string userName = Environment.UserName;
- var erm = new ExtractionRequestMessage
- {
- ExtractionJobIdentifier = jobId,
- ProjectNumber = _projectId,
- ExtractionDirectory = _extractionDir,
- JobSubmittedAt = now,
- IsIdentifiableExtraction = _isIdentifiableExtraction,
- IsNoFilterExtraction = _isNoFiltersExtraction,
- IsPooledExtraction = _isPooledExtraction,
-
- // TODO(rkm 2021-04-01) Change this to an ExtractionKey type
- KeyTag = extractionKey.ToString(),
-
- Modality = _modality,
-
- // NOTE(rkm 2021-04-01) Set below
- ExtractionIdentifiers = null!,
- };
-
- List ermList =
- idList
- .Chunk(_maxIdentifiersPerMessage)
- .Select(x =>
- new ExtractionRequestMessage(erm)
- {
- ExtractionIdentifiers = [.. x]
- }
- ).ToList();
-
- var erim = new ExtractionRequestInfoMessage
+ var erm = new ExtractionRequestMessage
+ {
+ ExtractionJobIdentifier = jobId,
+ ProjectNumber = _projectId,
+ ExtractionDirectory = _extractionDir,
+ JobSubmittedAt = now,
+ IsIdentifiableExtraction = _isIdentifiableExtraction,
+ IsNoFilterExtraction = _isNoFiltersExtraction,
+ IsPooledExtraction = _isPooledExtraction,
+
+ // TODO(rkm 2021-04-01) Change this to an ExtractionKey type
+ KeyTag = extractionKey.ToString(),
+
+ Modality = _modality,
+
+ // NOTE(rkm 2021-04-01) Set below
+ ExtractionIdentifiers = null!,
+ };
+
+ List ermList =
+ idList
+ .Chunk(_maxIdentifiersPerMessage)
+ .Select(x =>
+ new ExtractionRequestMessage(erm)
+ {
+ ExtractionIdentifiers = [.. x]
+ }
+ ).ToList();
+
+ var erim = new ExtractionRequestInfoMessage
+ {
+ ExtractionJobIdentifier = jobId,
+ ProjectNumber = _projectId,
+ ExtractionDirectory = _extractionDir,
+ Modality = _modality,
+ JobSubmittedAt = now,
+ IsIdentifiableExtraction = _isIdentifiableExtraction,
+ IsNoFilterExtraction = _isNoFiltersExtraction,
+ IsPooledExtraction = _isPooledExtraction,
+
+ KeyTag = extractionKey.ToString(),
+ KeyValueCount = idList.Count,
+ UserName = userName,
+ };
+
+ if (_nonInteractive)
+ {
+ LaunchExtraction(jobId, ermList, erim);
+ }
+ else
+ {
+ var sb = new StringBuilder();
+ sb.AppendLine();
+ sb.AppendLine($"ExtractionJobIdentifier: {jobId}");
+ sb.AppendLine($"Submitted: {now:u}");
+ sb.AppendLine($"ProjectNumber: {_projectId}");
+ sb.AppendLine($"ExtractionDirectory: {_extractionDir}");
+ sb.AppendLine($"Modality: {_modality}");
+ sb.AppendLine($"ExtractionKey: {extractionKey}");
+ sb.AppendLine($"IsIdentifiableExtraction: {_isIdentifiableExtraction}");
+ sb.AppendLine($"IsNoFilterExtraction: {_isNoFiltersExtraction}");
+ sb.AppendLine($"IsPooledExtraction: {_isPooledExtraction}");
+ sb.AppendLine($"UserName: {userName}");
+ sb.AppendLine($"KeyValueCount: {idList.Count}");
+ sb.AppendLine($"ExtractionRequestMessage count: {ermList.Count}");
+ _logger.Info(sb.ToString());
+ LogManager.Flush();
+ Console.WriteLine("Confirm you want to start an extract job with the above information");
+
+ string? key;
+ do
{
- ExtractionJobIdentifier = jobId,
- ProjectNumber = _projectId,
- ExtractionDirectory = _extractionDir,
- Modality = _modality,
- JobSubmittedAt = now,
- IsIdentifiableExtraction = _isIdentifiableExtraction,
- IsNoFilterExtraction = _isNoFiltersExtraction,
- IsPooledExtraction = _isPooledExtraction,
-
- KeyTag = extractionKey.ToString(),
- KeyValueCount = idList.Count,
- UserName = userName,
- };
-
- if (_nonInteractive)
+ Console.Write("[y/n]: ");
+ key = _consoleInput.GetNextLine()?.ToLower();
+ } while (key != "y" && key != "n");
+
+ if (key == "y")
{
LaunchExtraction(jobId, ermList, erim);
}
else
{
- var sb = new StringBuilder();
- sb.AppendLine();
- sb.AppendLine($"ExtractionJobIdentifier: {jobId}");
- sb.AppendLine($"Submitted: {now:u}");
- sb.AppendLine($"ProjectNumber: {_projectId}");
- sb.AppendLine($"ExtractionDirectory: {_extractionDir}");
- sb.AppendLine($"Modality: {_modality}");
- sb.AppendLine($"ExtractionKey: {extractionKey}");
- sb.AppendLine($"IsIdentifiableExtraction: {_isIdentifiableExtraction}");
- sb.AppendLine($"IsNoFilterExtraction: {_isNoFiltersExtraction}");
- sb.AppendLine($"IsPooledExtraction: {_isPooledExtraction}");
- sb.AppendLine($"UserName: {userName}");
- sb.AppendLine($"KeyValueCount: {idList.Count}");
- sb.AppendLine($"ExtractionRequestMessage count: {ermList.Count}");
- _logger.Info(sb.ToString());
- LogManager.Flush();
- Console.WriteLine("Confirm you want to start an extract job with the above information");
-
- string? key;
- do
- {
- Console.Write("[y/n]: ");
- key = _consoleInput.GetNextLine()?.ToLower();
- } while (key != "y" && key != "n");
-
- if (key == "y")
- {
- LaunchExtraction(jobId, ermList, erim);
- }
- else
- {
- _logger.Info("Operation cancelled by user");
- }
+ _logger.Info("Operation cancelled by user");
}
}
+ }
- private void LaunchExtraction(Guid jobId, IEnumerable ermList, ExtractionRequestInfoMessage erim)
- {
- InitialiseExtractionDir(jobId);
- SendMessagesImpl(ermList, erim);
- }
+ private void LaunchExtraction(Guid jobId, IEnumerable ermList, ExtractionRequestInfoMessage erim)
+ {
+ InitialiseExtractionDir(jobId);
+ SendMessagesImpl(ermList, erim);
+ }
- private void InitialiseExtractionDir(Guid jobId)
- {
- var absoluteExtractionDir = _fileSystem.Path.Combine(_extractionRoot, _extractionDir);
- _fileSystem.Directory.CreateDirectory(absoluteExtractionDir);
+ private void InitialiseExtractionDir(Guid jobId)
+ {
+ var absoluteExtractionDir = _fileSystem.Path.Combine(_extractionRoot, _extractionDir);
+ _fileSystem.Directory.CreateDirectory(absoluteExtractionDir);
- // Write the jobId to a file in the extraction dir to help identify the set of files if they are moved
- string jobIdFile = _fileSystem.Path.Combine(_extractionRoot, _extractionDir, "jobId.txt");
- _fileSystem.File.WriteAllText(jobIdFile, $"{jobId}\n");
+ // Write the jobId to a file in the extraction dir to help identify the set of files if they are moved
+ string jobIdFile = _fileSystem.Path.Combine(_extractionRoot, _extractionDir, "jobId.txt");
+ _fileSystem.File.WriteAllText(jobIdFile, $"{jobId}\n");
- _logger.Info("Created extraction directory and jobId file");
- }
+ _logger.Info("Created extraction directory and jobId file");
+ }
- private void SendMessagesImpl(IEnumerable ermList, ExtractionRequestInfoMessage erim)
- {
- _logger.Info("Sending messages");
+ private void SendMessagesImpl(IEnumerable ermList, ExtractionRequestInfoMessage erim)
+ {
+ _logger.Info("Sending messages");
- foreach (var msg in ermList)
- _extractionRequestProducer.SendMessage(msg, isInResponseTo: null, routingKey: null);
+ foreach (var msg in ermList)
+ _extractionRequestProducer.SendMessage(msg, isInResponseTo: null, routingKey: null);
- _extractionRequestInfoProducer.SendMessage(erim, isInResponseTo: null, routingKey: null);
+ _extractionRequestInfoProducer.SendMessage(erim, isInResponseTo: null, routingKey: null);
- _logger.Info("All messages sent");
- }
+ _logger.Info("All messages sent");
}
}
diff --git a/src/SmiServices/Applications/ExtractImages/IExtractionMessageSender.cs b/src/SmiServices/Applications/ExtractImages/IExtractionMessageSender.cs
index fc9325e3a..bd03d57ee 100644
--- a/src/SmiServices/Applications/ExtractImages/IExtractionMessageSender.cs
+++ b/src/SmiServices/Applications/ExtractImages/IExtractionMessageSender.cs
@@ -2,10 +2,9 @@
using System.Collections.Generic;
-namespace SmiServices.Applications.ExtractImages
+namespace SmiServices.Applications.ExtractImages;
+
+public interface IExtractionMessageSender
{
- public interface IExtractionMessageSender
- {
- void SendMessages(ExtractionKey extractionKey, List idList);
- }
+ void SendMessages(ExtractionKey extractionKey, List idList);
}
diff --git a/src/SmiServices/Applications/Setup/EnvironmentProbe.cs b/src/SmiServices/Applications/Setup/EnvironmentProbe.cs
index 68d8dca4a..81b3fb072 100644
--- a/src/SmiServices/Applications/Setup/EnvironmentProbe.cs
+++ b/src/SmiServices/Applications/Setup/EnvironmentProbe.cs
@@ -23,268 +23,267 @@
using System.Linq;
using System.Text;
-namespace SmiServices.Applications.Setup
-{
+namespace SmiServices.Applications.Setup;
- public class Probeable
- {
- public string Name { get; }
- public Func Run { get; }
- public string Category { get; }
- public CheckEventArgs? Result { get; set; }
- public Probeable(string name, Func run, string category)
- {
- Name = name;
- this.Run = run;
- Category = category;
- }
- }
+public class Probeable
+{
+ public string Name { get; }
+ public Func Run { get; }
+ public string Category { get; }
+ public CheckEventArgs? Result { get; set; }
- internal class EnvironmentProbe
+ public Probeable(string name, Func run, string category)
{
- public CheckEventArgs DeserializeYaml { get; }
- public GlobalOptions? Options { get; }
+ Name = name;
+ this.Run = run;
+ Category = category;
+ }
+}
+
+internal class EnvironmentProbe
+{
+ public CheckEventArgs DeserializeYaml { get; }
+ public GlobalOptions? Options { get; }
- public const string CheckInfrastructureTaskName = "Checking Infrastructure";
- public const string CheckMicroservicesTaskName = "Checking Microservices";
+ public const string CheckInfrastructureTaskName = "Checking Infrastructure";
+ public const string CheckMicroservicesTaskName = "Checking Microservices";
- public const string Infrastructure = "Infrastructure";
- public const string Microservices = "Microservices";
+ public const string Infrastructure = "Infrastructure";
+ public const string Microservices = "Microservices";
- public Dictionary Probes = [];
+ public Dictionary Probes = [];
- internal int GetExitCode()
+ internal int GetExitCode()
+ {
+ // get all things we can check
+ foreach (var prop in typeof(EnvironmentProbe).GetProperties())
{
- // get all things we can check
- foreach (var prop in typeof(EnvironmentProbe).GetProperties())
- {
- var val = prop.GetValue(this);
+ var val = prop.GetValue(this);
- // did any checks run
- if (val is CheckEventArgs cea)
+ // did any checks run
+ if (val is CheckEventArgs cea)
+ {
+ // that failed
+ if (cea.Result == CheckResult.Fail)
{
- // that failed
- if (cea.Result == CheckResult.Fail)
- {
- // something failed so exit code is failure (non zero)
- return 100;
- }
+ // something failed so exit code is failure (non zero)
+ return 100;
}
}
-
- return 0;
}
- public EnvironmentProbe(string? yamlFile)
- {
- Probes = [];
- Add(Infrastructure, "RabbitMq", ProbeRabbitMq);
- Add(Infrastructure, "MongoDb", ProbeMongoDb);
- Add(Infrastructure, "Rdmp", ProbeRdmp);
+ return 0;
+ }
+
+ public EnvironmentProbe(string? yamlFile)
+ {
+ Probes = [];
+ Add(Infrastructure, "RabbitMq", ProbeRabbitMq);
+ Add(Infrastructure, "MongoDb", ProbeMongoDb);
+ Add(Infrastructure, "Rdmp", ProbeRdmp);
- Add(Microservices, "CohortExtractor", () => Probe(nameof(CohortExtractorHost), (o) => new CohortExtractorHost(o, null, null)));
- Add(Microservices, "DicomAnonymiser", () => Probe(nameof(DicomAnonymiserHost), (o) => new DicomAnonymiserHost(o)));
- Add(Microservices, "IsIdentifiable", () => Probe(nameof(IsIdentifiableHost), (o) => new IsIdentifiableHost(o)));
- Add(Microservices, "CohortPackager", () => Probe(nameof(CohortPackagerHost), (o) => new CohortPackagerHost(o)));
- Add(Microservices, "DicomRelationalMapper", () => Probe(nameof(DicomRelationalMapperHost), (o) => new DicomRelationalMapperHost(o)));
- Add(Microservices, "IdentifierMapper", () => Probe(nameof(IdentifierMapperHost), (o) => new IdentifierMapperHost(o)));
- Add(Microservices, "MongoDbPopulator", () => Probe(nameof(MongoDbPopulatorHost), (o) => new MongoDbPopulatorHost(o)));
- Add(Microservices, "DicomTagReader", () => Probe(nameof(DicomTagReaderHost), (o) => new DicomTagReaderHost(o)));
+ Add(Microservices, "CohortExtractor", () => Probe(nameof(CohortExtractorHost), (o) => new CohortExtractorHost(o, null, null)));
+ Add(Microservices, "DicomAnonymiser", () => Probe(nameof(DicomAnonymiserHost), (o) => new DicomAnonymiserHost(o)));
+ Add(Microservices, "IsIdentifiable", () => Probe(nameof(IsIdentifiableHost), (o) => new IsIdentifiableHost(o)));
+ Add(Microservices, "CohortPackager", () => Probe(nameof(CohortPackagerHost), (o) => new CohortPackagerHost(o)));
+ Add(Microservices, "DicomRelationalMapper", () => Probe(nameof(DicomRelationalMapperHost), (o) => new DicomRelationalMapperHost(o)));
+ Add(Microservices, "IdentifierMapper", () => Probe(nameof(IdentifierMapperHost), (o) => new IdentifierMapperHost(o)));
+ Add(Microservices, "MongoDbPopulator", () => Probe(nameof(MongoDbPopulatorHost), (o) => new MongoDbPopulatorHost(o)));
+ Add(Microservices, "DicomTagReader", () => Probe(nameof(DicomTagReaderHost), (o) => new DicomTagReaderHost(o)));
- /*
- {
- get
+ /*
+{
+ get
DicomTagReader {
- get;
- MongoDbPopulator {
+ get;
+ MongoDbPopulator {
+ ge
+ IdentifierMapper {
ge
- IdentifierMapper {
- ge
- DicomRelationalMapper
- DicomAnonymiser {
- get
- IsIdentifiable {
+ DicomRelationalMapper
+ DicomAnonymiser {
+ get
+ IsIdentifiable {
+ get;
+ CohortPackager {
get;
- CohortPackager {
- get;
- */
- try
- {
- if (string.IsNullOrWhiteSpace(yamlFile))
- throw new Exception("You have not yet entered a path for yaml file");
+ */
+ try
+ {
+ if (string.IsNullOrWhiteSpace(yamlFile))
+ throw new Exception("You have not yet entered a path for yaml file");
- Options = new GlobalOptionsFactory().Load("Setup", yamlFile);
- DeserializeYaml = new CheckEventArgs("Deserialized Yaml File", CheckResult.Success);
- }
- catch (Exception ex)
- {
- DeserializeYaml = new CheckEventArgs("Failed to Deserialize Yaml File", CheckResult.Fail, ex);
- }
+ Options = new GlobalOptionsFactory().Load("Setup", yamlFile);
+ DeserializeYaml = new CheckEventArgs("Deserialized Yaml File", CheckResult.Success);
}
-
- private void Add(string category, string name, Func probeMethod)
+ catch (Exception ex)
{
- Probes.Add(name, new Probeable(name, probeMethod, category));
+ DeserializeYaml = new CheckEventArgs("Failed to Deserialize Yaml File", CheckResult.Fail, ex);
}
+ }
- internal void CheckInfrastructure(IDataLoadEventListener? listener = null)
- {
- var probes = Probes.Where(p => p.Value.Category == Infrastructure).ToArray();
+ private void Add(string category, string name, Func probeMethod)
+ {
+ Probes.Add(name, new Probeable(name, probeMethod, category));
+ }
- var sw = Stopwatch.StartNew();
- var max = probes.Length;
- var current = 0;
- var task = CheckInfrastructureTaskName;
+ internal void CheckInfrastructure(IDataLoadEventListener? listener = null)
+ {
+ var probes = Probes.Where(p => p.Value.Category == Infrastructure).ToArray();
- listener?.OnProgress(this, new ProgressEventArgs(task, new ProgressMeasurement(current, ProgressType.Records, max), sw.Elapsed));
+ var sw = Stopwatch.StartNew();
+ var max = probes.Length;
+ var current = 0;
+ var task = CheckInfrastructureTaskName;
- foreach (var p in probes)
- {
- // clear old result
- p.Value.Result = null;
- p.Value.Result = p.Value.Run();
+ listener?.OnProgress(this, new ProgressEventArgs(task, new ProgressMeasurement(current, ProgressType.Records, max), sw.Elapsed));
- listener?.OnProgress(this, new ProgressEventArgs(task, new ProgressMeasurement(++current, ProgressType.Records, max), sw.Elapsed));
- }
- }
- internal void CheckMicroservices(IDataLoadEventListener? listener = null)
+ foreach (var p in probes)
{
- var probes = Probes.Where(p => p.Value.Category == Microservices).ToArray();
+ // clear old result
+ p.Value.Result = null;
+ p.Value.Result = p.Value.Run();
- var sw = Stopwatch.StartNew();
- var max = probes.Length;
- var current = 0;
- var task = CheckMicroservicesTaskName;
+ listener?.OnProgress(this, new ProgressEventArgs(task, new ProgressMeasurement(++current, ProgressType.Records, max), sw.Elapsed));
+ }
+ }
+ internal void CheckMicroservices(IDataLoadEventListener? listener = null)
+ {
+ var probes = Probes.Where(p => p.Value.Category == Microservices).ToArray();
- listener?.OnProgress(this, new ProgressEventArgs(task, new ProgressMeasurement(current, ProgressType.Records, max), sw.Elapsed));
+ var sw = Stopwatch.StartNew();
+ var max = probes.Length;
+ var current = 0;
+ var task = CheckMicroservicesTaskName;
- foreach (var p in probes)
- {
- // clear old result
- p.Value.Result = null;
- p.Value.Result = p.Value.Run();
+ listener?.OnProgress(this, new ProgressEventArgs(task, new ProgressMeasurement(current, ProgressType.Records, max), sw.Elapsed));
- listener?.OnProgress(this, new ProgressEventArgs(task, new ProgressMeasurement(++current, ProgressType.Records, max), sw.Elapsed));
- }
+ foreach (var p in probes)
+ {
+ // clear old result
+ p.Value.Result = null;
+ p.Value.Result = p.Value.Run();
+
+ listener?.OnProgress(this, new ProgressEventArgs(task, new ProgressMeasurement(++current, ProgressType.Records, max), sw.Elapsed));
}
+ }
- public CheckEventArgs? ProbeRdmp()
+ public CheckEventArgs? ProbeRdmp()
+ {
+ try
{
- try
- {
- if (Options == null)
- return null;
+ if (Options == null)
+ return null;
- if (Options.RDMPOptions == null ||
+ if (Options.RDMPOptions == null ||
- // Must specify either SqlServer or file system backend for RDMP platform metadata
- (string.IsNullOrEmpty(Options.RDMPOptions.CatalogueConnectionString) &&
- string.IsNullOrWhiteSpace(Options.RDMPOptions.YamlDir)))
- {
- throw new Exception("No RDMP connection settings specified");
- }
+ // Must specify either SqlServer or file system backend for RDMP platform metadata
+ (string.IsNullOrEmpty(Options.RDMPOptions.CatalogueConnectionString) &&
+ string.IsNullOrWhiteSpace(Options.RDMPOptions.YamlDir)))
+ {
+ throw new Exception("No RDMP connection settings specified");
+ }
- var provider = Options.RDMPOptions.GetRepositoryProvider();
+ var provider = Options.RDMPOptions.GetRepositoryProvider();
- var startup = new Startup(provider);
+ var startup = new Startup(provider);
- var failed = false;
- var sb = new StringBuilder();
- var exceptions = new List();
+ var failed = false;
+ var sb = new StringBuilder();
+ var exceptions = new List();
- startup.DatabaseFound += (s, e) =>
- {
- failed = !failed && e.Status != RDMPPlatformDatabaseStatus.Healthy || e.Exception != null;
- sb.AppendLine($"{e.Patcher.Name} {e.Status}");
+ startup.DatabaseFound += (s, e) =>
+ {
+ failed = !failed && e.Status != RDMPPlatformDatabaseStatus.Healthy || e.Exception != null;
+ sb.AppendLine($"{e.Patcher.Name} {e.Status}");
- if (e.Exception != null)
- {
- sb.AppendLine(ExceptionHelper.ExceptionToListOfInnerMessages(e.Exception));
- exceptions.Add(e.Exception);
- }
- };
+ if (e.Exception != null)
+ {
+ sb.AppendLine(ExceptionHelper.ExceptionToListOfInnerMessages(e.Exception));
+ exceptions.Add(e.Exception);
+ }
+ };
- startup.DoStartup(ThrowImmediatelyCheckNotifier.Quiet);
+ startup.DoStartup(ThrowImmediatelyCheckNotifier.Quiet);
- return new CheckEventArgs(sb.ToString(), failed ? CheckResult.Fail : CheckResult.Success);
- }
- catch (Exception ex)
- {
- return new CheckEventArgs("Failed to connect to RDMP", CheckResult.Fail, ex);
- }
+ return new CheckEventArgs(sb.ToString(), failed ? CheckResult.Fail : CheckResult.Success);
}
-
- public CheckEventArgs? ProbeRabbitMq()
+ catch (Exception ex)
{
- if (Options?.RabbitOptions == null)
- return null;
+ return new CheckEventArgs("Failed to connect to RDMP", CheckResult.Fail, ex);
+ }
+ }
- try
- {
- var adapter = new RabbitMQBroker(Options.RabbitOptions, "setup");
+ public CheckEventArgs? ProbeRabbitMq()
+ {
+ if (Options?.RabbitOptions == null)
+ return null;
- return new CheckEventArgs("Connected to RabbitMq", CheckResult.Success);
- }
- catch (Exception ex)
- {
- return new CheckEventArgs("Failed to connect to RabbitMq", CheckResult.Fail, ex);
- }
- }
+ try
+ {
+ var adapter = new RabbitMQBroker(Options.RabbitOptions, "setup");
- public CheckEventArgs? ProbeMongoDb()
+ return new CheckEventArgs("Connected to RabbitMq", CheckResult.Success);
+ }
+ catch (Exception ex)
{
- if (Options?.MongoDatabases?.DicomStoreOptions == null)
- return null;
+ return new CheckEventArgs("Failed to connect to RabbitMq", CheckResult.Fail, ex);
+ }
+ }
- try
- {
- // this opens connection to the server and tests for collection existing
- _ = new MongoDbAdapter("Setup", Options.MongoDatabases.DicomStoreOptions,
- Options.MongoDbPopulatorOptions?.ImageCollection ?? throw new InvalidOperationException());
+ public CheckEventArgs? ProbeMongoDb()
+ {
+ if (Options?.MongoDatabases?.DicomStoreOptions == null)
+ return null;
+ try
+ {
+ // this opens connection to the server and tests for collection existing
+ _ = new MongoDbAdapter("Setup", Options.MongoDatabases.DicomStoreOptions,
+ Options.MongoDbPopulatorOptions?.ImageCollection ?? throw new InvalidOperationException());
- var mongoDbOptions = Options.MongoDatabases.ExtractionStoreOptions
- ?? throw new ArgumentException($"ExtractionStoreOptions was null");
- _ = new MongoExtractJobStore(
- MongoClientHelpers.GetMongoClient(mongoDbOptions, "Setup"),
- mongoDbOptions.DatabaseName ?? throw new InvalidOperationException(), new DateTimeProvider()
- );
+ var mongoDbOptions = Options.MongoDatabases.ExtractionStoreOptions
+ ?? throw new ArgumentException($"ExtractionStoreOptions was null");
- return new CheckEventArgs("MongoDb Checking Succeeded", CheckResult.Success);
- }
- catch (Exception ex)
- {
- return new CheckEventArgs("MongoDb Checking Failed", CheckResult.Fail, ex);
- }
- }
+ _ = new MongoExtractJobStore(
+ MongoClientHelpers.GetMongoClient(mongoDbOptions, "Setup"),
+ mongoDbOptions.DatabaseName ?? throw new InvalidOperationException(), new DateTimeProvider()
+ );
- private CheckEventArgs? Probe(string probeName, Func hostConstructor)
+ return new CheckEventArgs("MongoDb Checking Succeeded", CheckResult.Success);
+ }
+ catch (Exception ex)
{
- if (Options == null)
- return null;
+ return new CheckEventArgs("MongoDb Checking Failed", CheckResult.Fail, ex);
+ }
+ }
- try
- {
- var host = hostConstructor(Options);
+ private CheckEventArgs? Probe(string probeName, Func hostConstructor)
+ {
+ if (Options == null)
+ return null;
- host.StartAuxConnections();
- host.Start();
+ try
+ {
+ var host = hostConstructor(Options);
- host.Stop("Finished Testing");
+ host.StartAuxConnections();
+ host.Start();
- return new CheckEventArgs($"{probeName} Succeeded", CheckResult.Success);
- }
- catch (Exception ex)
- {
- return new CheckEventArgs($"{probeName} Failed", CheckResult.Fail, ex);
- }
- }
+ host.Stop("Finished Testing");
+ return new CheckEventArgs($"{probeName} Succeeded", CheckResult.Success);
+ }
+ catch (Exception ex)
+ {
+ return new CheckEventArgs($"{probeName} Failed", CheckResult.Fail, ex);
+ }
}
+
}
diff --git a/src/SmiServices/Applications/Setup/SetupIsolatedStorage.cs b/src/SmiServices/Applications/Setup/SetupIsolatedStorage.cs
index 9c2c59ca9..7903530eb 100644
--- a/src/SmiServices/Applications/Setup/SetupIsolatedStorage.cs
+++ b/src/SmiServices/Applications/Setup/SetupIsolatedStorage.cs
@@ -9,490 +9,489 @@
using System.IO.IsolatedStorage;
using System.Linq;
-namespace SmiServices.Applications.Setup
+namespace SmiServices.Applications.Setup;
+
+internal class SetupIsolatedStorage
{
- internal class SetupIsolatedStorage
- {
- private readonly IsolatedStorageFile store;
- private readonly object locker = new();
+ private readonly IsolatedStorageFile store;
+ private readonly object locker = new();
- public SetupIsolatedStorage()
+ public SetupIsolatedStorage()
+ {
+ try
{
- try
- {
- store = IsolatedStorageFile.GetUserStoreForApplication();
- }
- catch (Exception)
- {
- store = IsolatedStorageFile.GetUserStoreForAssembly();
- }
+ store = IsolatedStorageFile.GetUserStoreForApplication();
+ }
+ catch (Exception)
+ {
+ store = IsolatedStorageFile.GetUserStoreForAssembly();
}
+ }
- ///
- /// Add or Update
- ///
- ///
- ///
- ///
- ///
- private bool AddOrUpdateValueInternal(string key, T value)
+ ///
+ /// Add or Update
+ ///
+ ///
+ ///
+ ///
+ ///
+ private bool AddOrUpdateValueInternal(string key, T value)
+ {
+ if (value == null)
{
- if (value == null)
- {
- Remove(key);
+ Remove(key);
- return true;
- }
+ return true;
+ }
- var type = value.GetType();
+ var type = value.GetType();
- if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>))
- {
- type = type.GenericTypeArguments.FirstOrDefault();
- }
+ if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>))
+ {
+ type = type.GenericTypeArguments.FirstOrDefault();
+ }
- if ((type == typeof(string)) ||
- (type == typeof(decimal)) ||
- (type == typeof(double)) ||
- (type == typeof(float)) ||
- (type == typeof(DateTime)) ||
- (type == typeof(Guid)) ||
- (type == typeof(bool)) ||
- (type == typeof(int)) ||
- (type == typeof(long)) ||
- (type == typeof(byte)))
+ if ((type == typeof(string)) ||
+ (type == typeof(decimal)) ||
+ (type == typeof(double)) ||
+ (type == typeof(float)) ||
+ (type == typeof(DateTime)) ||
+ (type == typeof(Guid)) ||
+ (type == typeof(bool)) ||
+ (type == typeof(int)) ||
+ (type == typeof(long)) ||
+ (type == typeof(byte)))
+ {
+ lock (locker)
{
- lock (locker)
- {
- string? str;
-
- if (value is decimal)
- {
- return AddOrUpdateValue(key,
- Convert.ToString(Convert.ToDecimal(value), System.Globalization.CultureInfo.InvariantCulture));
- }
- else if (value is DateTime)
- {
- return AddOrUpdateValue(key,
- Convert.ToString(-(Convert.ToDateTime(value)).ToUniversalTime().Ticks,
- System.Globalization.CultureInfo.InvariantCulture));
- }
- else
- str = Convert.ToString(value, System.Globalization.CultureInfo.InvariantCulture);
+ string? str;
- string? oldValue = null;
+ if (value is decimal)
+ {
+ return AddOrUpdateValue(key,
+ Convert.ToString(Convert.ToDecimal(value), System.Globalization.CultureInfo.InvariantCulture));
+ }
+ else if (value is DateTime)
+ {
+ return AddOrUpdateValue(key,
+ Convert.ToString(-(Convert.ToDateTime(value)).ToUniversalTime().Ticks,
+ System.Globalization.CultureInfo.InvariantCulture));
+ }
+ else
+ str = Convert.ToString(value, System.Globalization.CultureInfo.InvariantCulture);
- if (store.FileExists(key))
- {
- using var stream = store.OpenFile(key, FileMode.Open);
- using var sr = new StreamReader(stream);
- oldValue = sr.ReadToEnd();
- }
+ string? oldValue = null;
- using (var stream = store.OpenFile(key, FileMode.Create, FileAccess.Write))
- {
- using var sw = new StreamWriter(stream);
- sw.Write(str);
- }
+ if (store.FileExists(key))
+ {
+ using var stream = store.OpenFile(key, FileMode.Open);
+ using var sr = new StreamReader(stream);
+ oldValue = sr.ReadToEnd();
+ }
- return oldValue != str;
+ using (var stream = store.OpenFile(key, FileMode.Create, FileAccess.Write))
+ {
+ using var sw = new StreamWriter(stream);
+ sw.Write(str);
}
- }
- throw new ArgumentException(string.Format("Value of type {0} is not supported.", type?.Name));
+ return oldValue != str;
+ }
}
- ///
- /// Get Value
- ///
- ///
- ///
- ///
- ///
- private T? GetValueOrDefaultInternal(string key, T? defaultValue = default)
+ throw new ArgumentException(string.Format("Value of type {0} is not supported.", type?.Name));
+ }
+
+ ///
+ /// Get Value
+ ///
+ ///
+ ///
+ ///
+ ///
+ private T? GetValueOrDefaultInternal(string key, T? defaultValue = default)
+ {
+ object? value = null;
+ lock (locker)
{
- object? value = null;
- lock (locker)
+ try
{
- try
- {
- string? str = null;
-
- // If the key exists, retrieve the value.
- if (store.FileExists(key))
- {
- using var stream = store.OpenFile(key, FileMode.Open);
- using var sr = new StreamReader(stream);
- str = sr.ReadToEnd();
- }
+ string? str = null;
- if (str == null)
- return defaultValue;
-
- var type = typeof(T);
+ // If the key exists, retrieve the value.
+ if (store.FileExists(key))
+ {
+ using var stream = store.OpenFile(key, FileMode.Open);
+ using var sr = new StreamReader(stream);
+ str = sr.ReadToEnd();
+ }
- if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>))
- {
- type = type.GenericTypeArguments.FirstOrDefault();
- }
+ if (str == null)
+ return defaultValue;
- if (type == typeof(string))
- value = str;
+ var type = typeof(T);
- else if (type == typeof(decimal))
- {
+ if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>))
+ {
+ type = type.GenericTypeArguments.FirstOrDefault();
+ }
- string savedDecimal = Convert.ToString(str);
+ if (type == typeof(string))
+ value = str;
+ else if (type == typeof(decimal))
+ {
- value = Convert.ToDecimal(savedDecimal, System.Globalization.CultureInfo.InvariantCulture);
+ string savedDecimal = Convert.ToString(str);
- return null != value ? (T)value : defaultValue;
- }
+ value = Convert.ToDecimal(savedDecimal, System.Globalization.CultureInfo.InvariantCulture);
- else if (type == typeof(double))
- {
- value = Convert.ToDouble(str, System.Globalization.CultureInfo.InvariantCulture);
- }
+ return null != value ? (T)value : defaultValue;
- else if (type == typeof(Single))
- {
- value = Convert.ToSingle(str, System.Globalization.CultureInfo.InvariantCulture);
- }
-
- else if (type == typeof(DateTime))
- {
-
- var ticks = Convert.ToInt64(str, System.Globalization.CultureInfo.InvariantCulture);
- if (ticks >= 0)
- {
- //Old value, stored before update to UTC values
- value = new DateTime(ticks);
- }
- else
- {
- //New value, UTC
- value = new DateTime(-ticks, DateTimeKind.Utc);
- }
+ }
+ else if (type == typeof(double))
+ {
+ value = Convert.ToDouble(str, System.Globalization.CultureInfo.InvariantCulture);
+ }
- return (T)value;
- }
+ else if (type == typeof(Single))
+ {
+ value = Convert.ToSingle(str, System.Globalization.CultureInfo.InvariantCulture);
+ }
- else if (type == typeof(Guid))
- {
- if (Guid.TryParse(str, out Guid guid))
- value = guid;
- }
+ else if (type == typeof(DateTime))
+ {
- else if (type == typeof(bool))
+ var ticks = Convert.ToInt64(str, System.Globalization.CultureInfo.InvariantCulture);
+ if (ticks >= 0)
{
- value = Convert.ToBoolean(str, System.Globalization.CultureInfo.InvariantCulture);
+ //Old value, stored before update to UTC values
+ value = new DateTime(ticks);
}
-
- else if (type == typeof(Int32))
+ else
{
- value = Convert.ToInt32(str, System.Globalization.CultureInfo.InvariantCulture);
+ //New value, UTC
+ value = new DateTime(-ticks, DateTimeKind.Utc);
}
- else if (type == typeof(Int64))
- {
- value = Convert.ToInt64(str, System.Globalization.CultureInfo.InvariantCulture);
- }
- else if (type == typeof(byte))
- {
- value = Convert.ToByte(str, System.Globalization.CultureInfo.InvariantCulture);
- }
+ return (T)value;
+ }
- else
- {
- throw new ArgumentException("Value of type " + type + " is not supported.");
- }
+ else if (type == typeof(Guid))
+ {
+ if (Guid.TryParse(str, out Guid guid))
+ value = guid;
}
- catch (FormatException)
+
+ else if (type == typeof(bool))
{
- return defaultValue;
+ value = Convert.ToBoolean(str, System.Globalization.CultureInfo.InvariantCulture);
}
- }
+ else if (type == typeof(Int32))
+ {
+ value = Convert.ToInt32(str, System.Globalization.CultureInfo.InvariantCulture);
+ }
- return null != value ? (T)value : defaultValue;
- }
+ else if (type == typeof(Int64))
+ {
+ value = Convert.ToInt64(str, System.Globalization.CultureInfo.InvariantCulture);
+ }
- ///
- /// Remove key
- ///
- /// Key to remove
- public void Remove(string key)
- {
- if (store.FileExists(key))
- store.DeleteFile(key);
- }
+ else if (type == typeof(byte))
+ {
+ value = Convert.ToByte(str, System.Globalization.CultureInfo.InvariantCulture);
+ }
- ///
- /// Clear all keys from settings
- ///
- public void Clear()
- {
- try
- {
- foreach (var file in store.GetFileNames())
+ else
{
- store.DeleteFile(file);
+ throw new ArgumentException("Value of type " + type + " is not supported.");
}
}
- catch (Exception ex)
+ catch (FormatException)
{
- Console.WriteLine("Unable to clear all defaults. Message: " + ex.Message);
+ return defaultValue;
}
- }
- ///
- /// Checks to see if the key has been added.
- ///
- /// Key to check
- /// True if contains key, else false
- public bool Contains(string key)
- {
- return store.FileExists(key);
}
- #region GetValueOrDefault
+ return null != value ? (T)value : defaultValue;
+ }
- ///
- /// Gets the current value or the default that you specify.
- ///
- /// Key for settings
- /// default value if not set
- /// Value or default
- public decimal GetValueOrDefault(string key, decimal defaultValue)
- {
- return
- GetValueOrDefaultInternal(key, defaultValue);
- }
+ ///
+ /// Remove key
+ ///
+ /// Key to remove
+ public void Remove(string key)
+ {
+ if (store.FileExists(key))
+ store.DeleteFile(key);
+ }
- ///
- /// Gets the current value or the default that you specify.
- ///
- /// Key for settings
- /// default value if not set
- /// Value or default
- public bool GetValueOrDefault(string key, bool defaultValue)
+ ///
+ /// Clear all keys from settings
+ ///
+ public void Clear()
+ {
+ try
{
- return
- GetValueOrDefaultInternal(key, defaultValue);
+ foreach (var file in store.GetFileNames())
+ {
+ store.DeleteFile(file);
+ }
}
-
- ///
- /// Gets the current value or the default that you specify.
- ///
- /// Key for settings
- /// default value if not set
- /// Value or default
- public long GetValueOrDefault(string key, long defaultValue)
+ catch (Exception ex)
{
- return
- GetValueOrDefaultInternal(key, defaultValue);
+ Console.WriteLine("Unable to clear all defaults. Message: " + ex.Message);
}
+ }
- ///
- /// Gets the current value or the default that you specify.
- ///
- /// Key for settings
- /// default value if not set
- /// Value or default
- public string? GetValueOrDefault(string key, string? defaultValue)
- {
- return GetValueOrDefaultInternal(key, defaultValue);
+ ///
+ /// Checks to see if the key has been added.
+ ///
+ /// Key to check
+ /// True if contains key, else false
+ public bool Contains(string key)
+ {
+ return store.FileExists(key);
+ }
- }
+ #region GetValueOrDefault
- ///
- /// Gets the current value or the default that you specify.
- ///
- /// Key for settings
- /// default value if not set
- /// Value or default
- public int GetValueOrDefault(string key, int defaultValue)
- {
- return
+ ///
+ /// Gets the current value or the default that you specify.
+ ///
+ /// Key for settings
+ /// default value if not set
+ /// Value or default
+ public decimal GetValueOrDefault(string key, decimal defaultValue)
+ {
+ return
GetValueOrDefaultInternal(key, defaultValue);
+ }
- }
+ ///
+ /// Gets the current value or the default that you specify.
+ ///
+ /// Key for settings
+ /// default value if not set
+ /// Value or default
+ public bool GetValueOrDefault(string key, bool defaultValue)
+ {
+ return
+ GetValueOrDefaultInternal(key, defaultValue);
+ }
- ///
- /// Gets the current value or the default that you specify.
- ///
- /// Key for settings
- /// default value if not set
- /// Value or default
- public float GetValueOrDefault(string key, float defaultValue)
- {
- return
- GetValueOrDefaultInternal(key, defaultValue);
+ ///
+ /// Gets the current value or the default that you specify.
+ ///
+ /// Key for settings
+ /// default value if not set
+ /// Value or default
+ public long GetValueOrDefault(string key, long defaultValue)
+ {
+ return
+ GetValueOrDefaultInternal(key, defaultValue);
+ }
- }
+ ///
+ /// Gets the current value or the default that you specify.
+ ///
+ /// Key for settings
+ /// default value if not set
+ /// Value or default
+ public string? GetValueOrDefault(string key, string? defaultValue)
+ {
+ return GetValueOrDefaultInternal(key, defaultValue);
- ///
- /// Gets the current value or the default that you specify.
- ///
- /// Key for settings
- /// default value if not set
- /// Value or default
- public DateTime GetValueOrDefault(string key, DateTime defaultValue)
- {
- return
- GetValueOrDefaultInternal(key, defaultValue);
+ }
- }
+ ///
+ /// Gets the current value or the default that you specify.
+ ///
+ /// Key for settings
+ /// default value if not set
+ /// Value or default
+ public int GetValueOrDefault(string key, int defaultValue)
+ {
+ return
+ GetValueOrDefaultInternal(key, defaultValue);
- ///
- /// Gets the current value or the default that you specify.
- ///
- /// Key for settings
- /// default value if not set
- /// Value or default
- public Guid GetValueOrDefault(string key, Guid defaultValue)
- {
- return
- GetValueOrDefaultInternal(key, defaultValue);
+ }
- }
+ ///
+ /// Gets the current value or the default that you specify.
+ ///
+ /// Key for settings
+ /// default value if not set
+ /// Value or default
+ public float GetValueOrDefault(string key, float defaultValue)
+ {
+ return
+ GetValueOrDefaultInternal(key, defaultValue);
- ///
- /// Gets the current value or the default that you specify.
- ///
- /// Key for settings
- /// default value if not set
- /// Value or default
- public double GetValueOrDefault(string key, double defaultValue)
- {
- return
- GetValueOrDefaultInternal(key, defaultValue);
- }
+ }
+
+ ///
+ /// Gets the current value or the default that you specify.
+ ///
+ /// Key for settings
+ /// default value if not set
+ /// Value or default
+ public DateTime GetValueOrDefault(string key, DateTime defaultValue)
+ {
+ return
+ GetValueOrDefaultInternal(key, defaultValue);
- #endregion
+ }
- #region AddOrUpdateValue
+ ///
+ /// Gets the current value or the default that you specify.
+ ///
+ /// Key for settings
+ /// default value if not set
+ /// Value or default
+ public Guid GetValueOrDefault(string key, Guid defaultValue)
+ {
+ return
+ GetValueOrDefaultInternal(key, defaultValue);
- ///
- /// Adds or updates the value
- ///
- /// Key for setting
- /// Value to set
- /// True of was added or updated and you need to save it.
- public bool AddOrUpdateValue(string key, decimal value)
- {
- return
- AddOrUpdateValueInternal(key, value);
+ }
- }
+ ///
+ /// Gets the current value or the default that you specify.
+ ///
+ /// Key for settings
+ /// default value if not set
+ /// Value or default
+ public double GetValueOrDefault(string key, double defaultValue)
+ {
+ return
+ GetValueOrDefaultInternal(key, defaultValue);
+ }
- ///
- /// Adds or updates the value
- ///
- /// Key for setting
- /// Value to set
- /// True of was added or updated and you need to save it.
- public bool AddOrUpdateValue(string key, bool value)
- {
- return
- AddOrUpdateValueInternal(key, value);
+ #endregion
- }
+ #region AddOrUpdateValue
- ///
- /// Adds or updates the value
- ///
- /// Key for setting
- /// Value to set
- /// True of was added or updated and you need to save it.
- public bool AddOrUpdateValue(string key, long value)
- {
- return AddOrUpdateValueInternal(key, value);
- }
+ ///
+ /// Adds or updates the value
+ ///
+ /// Key for setting
+ /// Value to set
+ /// True of was added or updated and you need to save it.
+ public bool AddOrUpdateValue(string key, decimal value)
+ {
+ return
+ AddOrUpdateValueInternal(key, value);
- ///
- /// Adds or updates the value
- ///
- /// Key for setting
- /// Value to set
- /// True of was added or updated and you need to save it.
- public bool AddOrUpdateValue(string key, string value)
- {
- return
- AddOrUpdateValueInternal(key, value);
+ }
- }
+ ///
+ /// Adds or updates the value
+ ///
+ /// Key for setting
+ /// Value to set
+ /// True of was added or updated and you need to save it.
+ public bool AddOrUpdateValue(string key, bool value)
+ {
+ return
+ AddOrUpdateValueInternal(key, value);
- ///
- /// Adds or updates the value
- ///
- /// Key for setting
- /// Value to set
- /// True of was added or updated and you need to save it.
- public bool AddOrUpdateValue(string key, int value)
- {
- return AddOrUpdateValueInternal(key, value);
- }
+ }
- ///
- /// Adds or updates the value
- ///
- /// Key for setting
- /// Value to set
- /// True of was added or updated and you need to save it.
- public bool AddOrUpdateValue(string key, float value)
- {
- return AddOrUpdateValueInternal(key, value);
- }
+ ///
+ /// Adds or updates the value
+ ///
+ /// Key for setting
+ /// Value to set
+ /// True of was added or updated and you need to save it.
+ public bool AddOrUpdateValue(string key, long value)
+ {
+ return AddOrUpdateValueInternal(key, value);
+ }
- ///
- /// Adds or updates the value
- ///
- /// Key for setting
- /// Value to set
- ///
- /// True of was added or updated and you need to save it.
- public bool AddOrUpdateValue(string key, DateTime value)
- {
- return AddOrUpdateValueInternal(key, value);
- }
+ ///
+ /// Adds or updates the value
+ ///
+ /// Key for setting
+ /// Value to set
+ /// True of was added or updated and you need to save it.
+ public bool AddOrUpdateValue(string key, string value)
+ {
+ return
+ AddOrUpdateValueInternal(key, value);
- ///
- /// Adds or updates the value
- ///
- /// Key for setting
- /// Value to set
- /// True of was added or updated and you need to save it.
- public bool AddOrUpdateValue(string key, Guid value)
- {
- return AddOrUpdateValueInternal(key, value);
- }
+ }
- ///
- /// Adds or updates the value
- ///
- /// Key for setting
- /// Value to set
- /// True of was added or updated and you need to save it.
- public bool AddOrUpdateValue(string key, double value)
- {
- return AddOrUpdateValueInternal(key, value);
- }
+ ///
+ /// Adds or updates the value
+ ///
+ /// Key for setting
+ /// Value to set
+ /// True of was added or updated and you need to save it.
+ public bool AddOrUpdateValue(string key, int value)
+ {
+ return AddOrUpdateValueInternal(key, value);
+ }
- #endregion
+ ///
+ /// Adds or updates the value
+ ///
+ /// Key for setting
+ /// Value to set
+ /// True of was added or updated and you need to save it.
+ public bool AddOrUpdateValue(string key, float value)
+ {
+ return AddOrUpdateValueInternal(key, value);
+ }
- ///
- /// Attempts to open the app settings page.
- ///
- /// true if success, else false and not supported
- public static bool OpenAppSettings()
- {
- return false;
- }
+ ///
+ /// Adds or updates the value
+ ///
+ /// Key for setting
+ /// Value to set
+ ///
+ /// True of was added or updated and you need to save it.
+ public bool AddOrUpdateValue(string key, DateTime value)
+ {
+ return AddOrUpdateValueInternal(key, value);
+ }
+
+ ///
+ /// Adds or updates the value
+ ///
+ /// Key for setting
+ /// Value to set
+ /// True of was added or updated and you need to save it.
+ public bool AddOrUpdateValue(string key, Guid value)
+ {
+ return AddOrUpdateValueInternal(key, value);
+ }
+
+ ///
+ /// Adds or updates the value
+ ///
+ /// Key for setting
+ /// Value to set
+ /// True of was added or updated and you need to save it.
+ public bool AddOrUpdateValue(string key, double value)
+ {
+ return AddOrUpdateValueInternal(key, value);
+ }
+
+ #endregion
+
+ ///
+ /// Attempts to open the app settings page.
+ ///
+ /// true if success, else false and not supported
+ public static bool OpenAppSettings()
+ {
+ return false;
}
}
diff --git a/src/SmiServices/Applications/Setup/SetupSettings.cs b/src/SmiServices/Applications/Setup/SetupSettings.cs
index 8cb140556..8fdeaa2c0 100644
--- a/src/SmiServices/Applications/Setup/SetupSettings.cs
+++ b/src/SmiServices/Applications/Setup/SetupSettings.cs
@@ -6,41 +6,39 @@
using System;
-namespace SmiServices.Applications.Setup
+namespace SmiServices.Applications.Setup;
+
+///
+/// This is the Settings static class that can be used in your Core solution or in any
+/// of your client applications. All settings are laid out the same exact way with getters
+/// and setters.
+///
+public static class SetupSettings
{
- ///
- /// This is the Settings static class that can be used in your Core solution or in any
- /// of your client applications. All settings are laid out the same exact way with getters
- /// and setters.
- ///
- public static class SetupSettings
- {
- static readonly Lazy _implementation = new(static () => CreateSettings(), System.Threading.LazyThreadSafetyMode.PublicationOnly);
-
- private static SetupIsolatedStorage AppSettings
- {
- get
- {
- SetupIsolatedStorage ret = _implementation.Value ?? throw new NotImplementedException("Isolated Storage does not work in this environment...");
- return ret;
- }
- }
+ static readonly Lazy _implementation = new(static () => CreateSettings(), System.Threading.LazyThreadSafetyMode.PublicationOnly);
- ///
- /// Last loaded/selected .yaml file
- ///
- internal static string YamlFile
+ private static SetupIsolatedStorage AppSettings
+ {
+ get
{
- get => AppSettings?.GetValueOrDefault("YamlFile", "") ?? throw new InvalidOperationException("AppSettings not yet initialised");
- set => AppSettings.AddOrUpdateValue("YamlFile", value);
+ SetupIsolatedStorage ret = _implementation.Value ?? throw new NotImplementedException("Isolated Storage does not work in this environment...");
+ return ret;
}
+ }
+ ///
+ /// Last loaded/selected .yaml file
+ ///
+ internal static string YamlFile
+ {
+ get => AppSettings?.GetValueOrDefault("YamlFile", "") ?? throw new InvalidOperationException("AppSettings not yet initialised");
+ set => AppSettings.AddOrUpdateValue("YamlFile", value);
+ }
- private static SetupIsolatedStorage CreateSettings()
- {
- return new SetupIsolatedStorage();
- }
+ private static SetupIsolatedStorage CreateSettings()
+ {
+ return new SetupIsolatedStorage();
}
}
diff --git a/src/SmiServices/Applications/TriggerUpdates/ITriggerUpdatesSource.cs b/src/SmiServices/Applications/TriggerUpdates/ITriggerUpdatesSource.cs
index 6100c02d7..b2e7eb935 100644
--- a/src/SmiServices/Applications/TriggerUpdates/ITriggerUpdatesSource.cs
+++ b/src/SmiServices/Applications/TriggerUpdates/ITriggerUpdatesSource.cs
@@ -2,19 +2,18 @@
using System.Collections.Generic;
-namespace SmiServices.Applications.TriggerUpdates
+namespace SmiServices.Applications.TriggerUpdates;
+
+public interface ITriggerUpdatesSource
{
- public interface ITriggerUpdatesSource
- {
- ///
- /// Returns updates to issue if any
- ///
- ///
- IEnumerable GetUpdates();
+ ///
+ /// Returns updates to issue if any
+ ///
+ ///
+ IEnumerable GetUpdates();
- ///
- /// Notifies the source that it should cancel any ongoing queries and attempt to stop issuing updates
- ///
- void Stop();
- }
+ ///
+ /// Notifies the source that it should cancel any ongoing queries and attempt to stop issuing updates
+ ///
+ void Stop();
}
diff --git a/src/SmiServices/Applications/TriggerUpdates/MapperSource.cs b/src/SmiServices/Applications/TriggerUpdates/MapperSource.cs
index 2db41e823..b0f604f54 100644
--- a/src/SmiServices/Applications/TriggerUpdates/MapperSource.cs
+++ b/src/SmiServices/Applications/TriggerUpdates/MapperSource.cs
@@ -14,246 +14,245 @@
using System.Threading;
-namespace SmiServices.Applications.TriggerUpdates
-{
- public class MapperSource : ITriggerUpdatesSource
- {
- private readonly ISwapIdentifiers _swapper;
- private readonly TriggerUpdatesFromMapperOptions _cliOptions;
- private readonly GlobalOptions _globalOptions;
+namespace SmiServices.Applications.TriggerUpdates;
- protected readonly CancellationTokenSource TokenSource = new();
- protected readonly ILogger Logger = LogManager.GetCurrentClassLogger();
+public class MapperSource : ITriggerUpdatesSource
+{
+ private readonly ISwapIdentifiers _swapper;
+ private readonly TriggerUpdatesFromMapperOptions _cliOptions;
+ private readonly GlobalOptions _globalOptions;
- ///
- /// True if the database querying operation is currently executing
- ///
- public bool IsExecuting { get; private set; } = false;
+ protected readonly CancellationTokenSource TokenSource = new();
+ protected readonly ILogger Logger = LogManager.GetCurrentClassLogger();
- private DbCommand? _currentCommandMainTable;
- private DbCommand? _currentCommandOtherTables;
+ ///
+ /// True if the database querying operation is currently executing
+ ///
+ public bool IsExecuting { get; private set; } = false;
- public MapperSource(GlobalOptions globalOptions, TriggerUpdatesFromMapperOptions cliOptions)
- {
- _cliOptions = cliOptions;
- _globalOptions = globalOptions;
+ private DbCommand? _currentCommandMainTable;
+ private DbCommand? _currentCommandOtherTables;
- FansiImplementations.Load();
+ public MapperSource(GlobalOptions globalOptions, TriggerUpdatesFromMapperOptions cliOptions)
+ {
+ _cliOptions = cliOptions;
+ _globalOptions = globalOptions;
- ISwapIdentifiers? swapper;
- try
- {
- var objectFactory = new MicroserviceObjectFactory();
- swapper = objectFactory.CreateInstance(globalOptions.IdentifierMapperOptions!.SwapperType!, typeof(ISwapIdentifiers).Assembly);
- }
- catch (Exception ex)
- {
- throw new Exception($"Could not create IdentifierMapper Swapper with SwapperType:{globalOptions.IdentifierMapperOptions?.SwapperType ?? "Null"}", ex);
- }
+ FansiImplementations.Load();
- _swapper = swapper ?? throw new ArgumentException("No SwapperType has been specified in GlobalOptions.IdentifierMapperOptions");
+ ISwapIdentifiers? swapper;
+ try
+ {
+ var objectFactory = new MicroserviceObjectFactory();
+ swapper = objectFactory.CreateInstance(globalOptions.IdentifierMapperOptions!.SwapperType!, typeof(ISwapIdentifiers).Assembly);
}
+ catch (Exception ex)
+ {
+ throw new Exception($"Could not create IdentifierMapper Swapper with SwapperType:{globalOptions.IdentifierMapperOptions?.SwapperType ?? "Null"}", ex);
+ }
+
+ _swapper = swapper ?? throw new ArgumentException("No SwapperType has been specified in GlobalOptions.IdentifierMapperOptions");
+ }
- public IEnumerable GetUpdates()
+ public IEnumerable GetUpdates()
+ {
+ IsExecuting = true;
+
+ try
{
- IsExecuting = true;
+ var mappingTable = _globalOptions.IdentifierMapperOptions!.Discover();
- try
- {
- var mappingTable = _globalOptions.IdentifierMapperOptions!.Discover();
+ if (!mappingTable.Exists())
+ throw new Exception($"Mapping table {mappingTable.GetFullyQualifiedName()} did not exist");
- if (!mappingTable.Exists())
- throw new Exception($"Mapping table {mappingTable.GetFullyQualifiedName()} did not exist");
+ var syntaxHelper = mappingTable.GetQuerySyntaxHelper();
- var syntaxHelper = mappingTable.GetQuerySyntaxHelper();
+ var archiveTableName = mappingTable.GetRuntimeName() + "_Archive";
+ var archiveTable = mappingTable.Database.ExpectTable(syntaxHelper.EnsureWrapped(archiveTableName), schema: mappingTable.Schema);
- var archiveTableName = mappingTable.GetRuntimeName() + "_Archive";
- var archiveTable = mappingTable.Database.ExpectTable(syntaxHelper.EnsureWrapped(archiveTableName), schema: mappingTable.Schema);
+ //may be null!
+ var guidTable = _swapper.GetGuidTableIfAny(_globalOptions.IdentifierMapperOptions);
- //may be null!
- var guidTable = _swapper.GetGuidTableIfAny(_globalOptions.IdentifierMapperOptions);
+ if (!archiveTable.Exists())
+ throw new Exception($"No Archive table exists for mapping table {mappingTable.GetFullyQualifiedName()}");
- if (!archiveTable.Exists())
- throw new Exception($"No Archive table exists for mapping table {mappingTable.GetFullyQualifiedName()}");
+ var swapCol = _globalOptions.IdentifierMapperOptions.SwapColumnName!;
+ var forCol = _globalOptions.IdentifierMapperOptions.ReplacementColumnName!;
- var swapCol = _globalOptions.IdentifierMapperOptions.SwapColumnName!;
- var forCol = _globalOptions.IdentifierMapperOptions.ReplacementColumnName!;
+ // may be null!
+ var liveDatabaseFieldName = _cliOptions.LiveDatabaseFieldName;
- // may be null!
- var liveDatabaseFieldName = _cliOptions.LiveDatabaseFieldName;
+ var archiveFetchSql = GetArchiveFetchSql(archiveTable, swapCol, forCol);
- var archiveFetchSql = GetArchiveFetchSql(archiveTable, swapCol, forCol);
+ using var con = mappingTable.Database.Server.GetConnection();
+ con.Open();
- using var con = mappingTable.Database.Server.GetConnection();
- con.Open();
+ var dateOfLastUpdate = _cliOptions.DateOfLastUpdate;
- var dateOfLastUpdate = _cliOptions.DateOfLastUpdate;
+ //find all records in the table that are new
+ var cmd = mappingTable.GetCommand($"SELECT {syntaxHelper.EnsureWrapped(swapCol)}, {syntaxHelper.EnsureWrapped(forCol)} FROM {mappingTable.GetFullyQualifiedName()} WHERE {syntaxHelper.EnsureWrapped(SpecialFieldNames.ValidFrom)} >= @dateOfLastUpdate", con);
+ cmd.CommandTimeout = _globalOptions.TriggerUpdatesOptions!.CommandTimeoutInSeconds;
- //find all records in the table that are new
- var cmd = mappingTable.GetCommand($"SELECT {syntaxHelper.EnsureWrapped(swapCol)}, {syntaxHelper.EnsureWrapped(forCol)} FROM {mappingTable.GetFullyQualifiedName()} WHERE {syntaxHelper.EnsureWrapped(SpecialFieldNames.ValidFrom)} >= @dateOfLastUpdate", con);
- cmd.CommandTimeout = _globalOptions.TriggerUpdatesOptions!.CommandTimeoutInSeconds;
+ mappingTable.Database.Server.AddParameterWithValueToCommand("@dateOfLastUpdate", cmd, dateOfLastUpdate);
- mappingTable.Database.Server.AddParameterWithValueToCommand("@dateOfLastUpdate", cmd, dateOfLastUpdate);
+ _currentCommandMainTable = cmd;
- _currentCommandMainTable = cmd;
+ TokenSource.Token.ThrowIfCancellationRequested();
+ using var r = cmd.ExecuteReader();
+ while (r.Read())
+ {
TokenSource.Token.ThrowIfCancellationRequested();
- using var r = cmd.ExecuteReader();
- while (r.Read())
- {
- TokenSource.Token.ThrowIfCancellationRequested();
-
- var currentSwapColValue = r[swapCol];
- var currentForColValue = r[forCol];
+ var currentSwapColValue = r[swapCol];
+ var currentForColValue = r[forCol];
- //if there is a corresponding record in the archive table
- using (var con2 = archiveTable.Database.Server.GetConnection())
- {
- con2.Open();
- var cmd2 = archiveTable.GetCommand(archiveFetchSql, con2);
- cmd2.CommandTimeout = _globalOptions.TriggerUpdatesOptions.CommandTimeoutInSeconds;
- _currentCommandOtherTables = cmd2;
+ //if there is a corresponding record in the archive table
+ using (var con2 = archiveTable.Database.Server.GetConnection())
+ {
+ con2.Open();
+ var cmd2 = archiveTable.GetCommand(archiveFetchSql, con2);
+ cmd2.CommandTimeout = _globalOptions.TriggerUpdatesOptions.CommandTimeoutInSeconds;
+ _currentCommandOtherTables = cmd2;
- archiveTable.Database.Server.AddParameterWithValueToCommand("@currentSwapColValue", cmd2, currentSwapColValue);
+ archiveTable.Database.Server.AddParameterWithValueToCommand("@currentSwapColValue", cmd2, currentSwapColValue);
- var oldForColValue = cmd2.ExecuteScalar();
+ var oldForColValue = cmd2.ExecuteScalar();
- TokenSource.Token.ThrowIfCancellationRequested();
+ TokenSource.Token.ThrowIfCancellationRequested();
- //if there is an entry in the archive for this old one then it is not a brand new record i.e. it is an update
- if (oldForColValue != null)
+ //if there is an entry in the archive for this old one then it is not a brand new record i.e. it is an update
+ if (oldForColValue != null)
+ {
+ //there is an entry in the archive so we need to issue a database update to update the live tables so the old archive
+ // table swap value (e.g. ECHI) is updated to the new one in the live table
+ yield return new UpdateValuesMessage
{
- //there is an entry in the archive so we need to issue a database update to update the live tables so the old archive
- // table swap value (e.g. ECHI) is updated to the new one in the live table
- yield return new UpdateValuesMessage
- {
- WhereFields = [liveDatabaseFieldName ?? forCol],
- HaveValues = [Qualify(oldForColValue)],
-
- WriteIntoFields = [liveDatabaseFieldName ?? forCol],
- Values = [Qualify(currentForColValue)]
- };
- }
+ WhereFields = [liveDatabaseFieldName ?? forCol],
+ HaveValues = [Qualify(oldForColValue)],
+
+ WriteIntoFields = [liveDatabaseFieldName ?? forCol],
+ Values = [Qualify(currentForColValue)]
+ };
}
+ }
- TokenSource.Token.ThrowIfCancellationRequested();
+ TokenSource.Token.ThrowIfCancellationRequested();
- // We should also look at guid mappings that are filled in now because of brand new records
- if (guidTable != null)
- {
- string guidFetchSql = $"SELECT {syntaxHelper.EnsureWrapped(TableLookupWithGuidFallbackSwapper.GuidColumnName)} FROM {guidTable.GetFullyQualifiedName()} WHERE {syntaxHelper.EnsureWrapped(swapCol)}=@currentSwapColValue";
+ // We should also look at guid mappings that are filled in now because of brand new records
+ if (guidTable != null)
+ {
+ string guidFetchSql = $"SELECT {syntaxHelper.EnsureWrapped(TableLookupWithGuidFallbackSwapper.GuidColumnName)} FROM {guidTable.GetFullyQualifiedName()} WHERE {syntaxHelper.EnsureWrapped(swapCol)}=@currentSwapColValue";
- using var con3 = guidTable.Database.Server.GetConnection();
- con3.Open();
- var cmd3 = guidTable.GetCommand(guidFetchSql, con3);
- cmd3.CommandTimeout = _globalOptions.TriggerUpdatesOptions.CommandTimeoutInSeconds;
- _currentCommandOtherTables = cmd3;
+ using var con3 = guidTable.Database.Server.GetConnection();
+ con3.Open();
+ var cmd3 = guidTable.GetCommand(guidFetchSql, con3);
+ cmd3.CommandTimeout = _globalOptions.TriggerUpdatesOptions.CommandTimeoutInSeconds;
+ _currentCommandOtherTables = cmd3;
- guidTable.Database.Server.AddParameterWithValueToCommand("@currentSwapColValue", cmd3, currentSwapColValue);
+ guidTable.Database.Server.AddParameterWithValueToCommand("@currentSwapColValue", cmd3, currentSwapColValue);
- var oldTemporaryMapping = cmd3.ExecuteScalar();
+ var oldTemporaryMapping = cmd3.ExecuteScalar();
- TokenSource.Token.ThrowIfCancellationRequested();
+ TokenSource.Token.ThrowIfCancellationRequested();
- //if this brand new mapping has a temporary guid assigned to it we need to issue an update of the temporary guid to the legit new mapping
- if (oldTemporaryMapping != null)
+ //if this brand new mapping has a temporary guid assigned to it we need to issue an update of the temporary guid to the legit new mapping
+ if (oldTemporaryMapping != null)
+ {
+ yield return new UpdateValuesMessage
{
- yield return new UpdateValuesMessage
- {
- WhereFields = [liveDatabaseFieldName ?? forCol],
- HaveValues = [Qualify(oldTemporaryMapping)],
-
- WriteIntoFields = [liveDatabaseFieldName ?? forCol],
- Values = [Qualify(currentForColValue)]
- };
- }
+ WhereFields = [liveDatabaseFieldName ?? forCol],
+ HaveValues = [Qualify(oldTemporaryMapping)],
+
+ WriteIntoFields = [liveDatabaseFieldName ?? forCol],
+ Values = [Qualify(currentForColValue)]
+ };
}
}
}
- finally
- {
- IsExecuting = false;
- }
}
-
- ///
- /// Returns DBMS formatted representation for constant
- ///
- ///
- ///
- private string Qualify(object value)
+ finally
{
- if (value == DBNull.Value || string.IsNullOrWhiteSpace(value?.ToString()))
- return "null";
-
- if (_cliOptions.Qualifier != '\0')
- return _cliOptions.Qualifier + value.ToString() + _cliOptions.Qualifier;
-
- return value.ToString() ?? throw new ArgumentException("Couldn't convert value to string");
+ IsExecuting = false;
}
+ }
- ///
- /// Returns a query for fetching the latest entry in the archive that matches a given private identifier (query contains parameter @currentSwapColValue)
- ///
- ///
- /// The private identifier column name e.g. CHI
- /// The public release identifier column name e.g. ECHI
- /// SQL for fetching the latest release identifier value (e.g. ECHI value) from the archive
- private static string GetArchiveFetchSql(DiscoveredTable archiveTable, string swapCol, string forCol)
- {
- // Work out how to get the latest entry in the _Archive table that corresponds to a given private identifier (e.g. CHI)
- var syntax = archiveTable.Database.Server.GetQuerySyntaxHelper();
+ ///
+ /// Returns DBMS formatted representation for constant
+ ///
+ ///
+ ///
+ private string Qualify(object value)
+ {
+ if (value == DBNull.Value || string.IsNullOrWhiteSpace(value?.ToString()))
+ return "null";
- var topX = syntax.HowDoWeAchieveTopX(1);
+ if (_cliOptions.Qualifier != '\0')
+ return _cliOptions.Qualifier + value.ToString() + _cliOptions.Qualifier;
- StringBuilder sb = new();
- sb.AppendLine("SELECT ");
+ return value.ToString() ?? throw new ArgumentException("Couldn't convert value to string");
+ }
- if (topX.Location == QueryComponent.SELECT)
- sb.AppendLine(topX.SQL);
+ ///
+ /// Returns a query for fetching the latest entry in the archive that matches a given private identifier (query contains parameter @currentSwapColValue)
+ ///
+ ///
+ /// The private identifier column name e.g. CHI
+ /// The public release identifier column name e.g. ECHI
+ /// SQL for fetching the latest release identifier value (e.g. ECHI value) from the archive
+ private static string GetArchiveFetchSql(DiscoveredTable archiveTable, string swapCol, string forCol)
+ {
+ // Work out how to get the latest entry in the _Archive table that corresponds to a given private identifier (e.g. CHI)
+ var syntax = archiveTable.Database.Server.GetQuerySyntaxHelper();
- sb.AppendLine(syntax.EnsureWrapped(forCol));
- sb.AppendLine("FROM " + archiveTable.GetFullyQualifiedName());
- sb.AppendLine("WHERE");
- sb.AppendLine($"{syntax.EnsureWrapped(swapCol)} = @currentSwapColValue");
+ var topX = syntax.HowDoWeAchieveTopX(1);
- if (topX.Location == QueryComponent.WHERE)
- {
- sb.AppendLine("AND");
- sb.AppendLine(topX.SQL);
- }
+ StringBuilder sb = new();
+ sb.AppendLine("SELECT ");
- sb.AppendLine("ORDER BY");
- sb.AppendLine(syntax.EnsureWrapped(SpecialFieldNames.ValidFrom) + " desc");
+ if (topX.Location == QueryComponent.SELECT)
+ sb.AppendLine(topX.SQL);
- if (topX.Location == QueryComponent.Postfix)
- sb.AppendLine(topX.SQL);
+ sb.AppendLine(syntax.EnsureWrapped(forCol));
+ sb.AppendLine("FROM " + archiveTable.GetFullyQualifiedName());
+ sb.AppendLine("WHERE");
+ sb.AppendLine($"{syntax.EnsureWrapped(swapCol)} = @currentSwapColValue");
- return sb.ToString();
+ if (topX.Location == QueryComponent.WHERE)
+ {
+ sb.AppendLine("AND");
+ sb.AppendLine(topX.SQL);
}
- public void Stop()
- {
- TokenSource.Cancel();
+ sb.AppendLine("ORDER BY");
+ sb.AppendLine(syntax.EnsureWrapped(SpecialFieldNames.ValidFrom) + " desc");
+
+ if (topX.Location == QueryComponent.Postfix)
+ sb.AppendLine(topX.SQL);
- _currentCommandMainTable?.Cancel();
+ return sb.ToString();
+ }
- _currentCommandOtherTables?.Cancel();
+ public void Stop()
+ {
+ TokenSource.Cancel();
- // give application 10 seconds to exit
- var timeout = 10_000;
- const int delta = 500;
- while (IsExecuting && timeout > 0)
- {
- Thread.Sleep(delta);
- timeout -= delta;
- }
+ _currentCommandMainTable?.Cancel();
- if (timeout <= 0)
- throw new ApplicationException("Query execution did not exit in time");
+ _currentCommandOtherTables?.Cancel();
- Logger.Info("Query execution aborted, exiting");
+ // give application 10 seconds to exit
+ var timeout = 10_000;
+ const int delta = 500;
+ while (IsExecuting && timeout > 0)
+ {
+ Thread.Sleep(delta);
+ timeout -= delta;
}
+
+ if (timeout <= 0)
+ throw new ApplicationException("Query execution did not exit in time");
+
+ Logger.Info("Query execution aborted, exiting");
}
}
diff --git a/src/SmiServices/Applications/TriggerUpdates/TriggerUpdates.cs b/src/SmiServices/Applications/TriggerUpdates/TriggerUpdates.cs
index acd187d16..b8d2a2823 100644
--- a/src/SmiServices/Applications/TriggerUpdates/TriggerUpdates.cs
+++ b/src/SmiServices/Applications/TriggerUpdates/TriggerUpdates.cs
@@ -5,38 +5,37 @@
using System.Diagnostics.CodeAnalysis;
-namespace SmiServices.Applications.TriggerUpdates
+namespace SmiServices.Applications.TriggerUpdates;
+
+public static class TriggerUpdates
{
- public static class TriggerUpdates
+ [ExcludeFromCodeCoverage]
+ public static int Main(IEnumerable args)
{
- [ExcludeFromCodeCoverage]
- public static int Main(IEnumerable args)
- {
- int ret = SmiCliInit
- .ParseAndRun(
- args,
- nameof(TriggerUpdates),
- [
- typeof(TriggerUpdatesFromMapperOptions),
- ],
- OnParse
- );
- return ret;
- }
+ int ret = SmiCliInit
+ .ParseAndRun(
+ args,
+ nameof(TriggerUpdates),
+ [
+ typeof(TriggerUpdatesFromMapperOptions),
+ ],
+ OnParse
+ );
+ return ret;
+ }
- private static int OnParse(GlobalOptions globals, object opts)
- {
- var parsedOptions = SmiCliInit.Verify(opts);
+ private static int OnParse(GlobalOptions globals, object opts)
+ {
+ var parsedOptions = SmiCliInit.Verify(opts);
- ITriggerUpdatesSource source = parsedOptions switch
- {
- TriggerUpdatesFromMapperOptions o => new MapperSource(globals, o),
- _ => throw new NotImplementedException($"No case for '{parsedOptions.GetType()}'")
- };
+ ITriggerUpdatesSource source = parsedOptions switch
+ {
+ TriggerUpdatesFromMapperOptions o => new MapperSource(globals, o),
+ _ => throw new NotImplementedException($"No case for '{parsedOptions.GetType()}'")
+ };
- var bootstrapper = new MicroserviceHostBootstrapper(() => new TriggerUpdatesHost(globals, source));
- int ret = bootstrapper.Main();
- return ret;
- }
+ var bootstrapper = new MicroserviceHostBootstrapper(() => new TriggerUpdatesHost(globals, source));
+ int ret = bootstrapper.Main();
+ return ret;
}
}
diff --git a/src/SmiServices/Applications/TriggerUpdates/TriggerUpdatesFromMapperOptions.cs b/src/SmiServices/Applications/TriggerUpdates/TriggerUpdatesFromMapperOptions.cs
index 88a971ea5..18fcc979d 100644
--- a/src/SmiServices/Applications/TriggerUpdates/TriggerUpdatesFromMapperOptions.cs
+++ b/src/SmiServices/Applications/TriggerUpdates/TriggerUpdatesFromMapperOptions.cs
@@ -2,19 +2,18 @@
using System;
-namespace SmiServices.Applications.TriggerUpdates
+namespace SmiServices.Applications.TriggerUpdates;
+
+[Verb("mapper", HelpText = "Triggers updates based on new identifier mapping table updates")]
+public class TriggerUpdatesFromMapperOptions : TriggerUpdatesCliOptions
{
- [Verb("mapper", HelpText = "Triggers updates based on new identifier mapping table updates")]
- public class TriggerUpdatesFromMapperOptions : TriggerUpdatesCliOptions
- {
- [Option('d', "DateOfLastUpdate", Required = true, HelpText = "The last known date where live tables and mapping table were in sync. Updates will be issued for records changed after this date")]
- public DateTime DateOfLastUpdate { get; set; }
+ [Option('d', "DateOfLastUpdate", Required = true, HelpText = "The last known date where live tables and mapping table were in sync. Updates will be issued for records changed after this date")]
+ public DateTime DateOfLastUpdate { get; set; }
- [Option('f', "FieldName", HelpText = "The field name of the release identifier in your databases e.g. PatientID. Only needed if different from the mapping table swap column name e.g. ECHI")]
- public string? LiveDatabaseFieldName { get; set; }
+ [Option('f', "FieldName", HelpText = "The field name of the release identifier in your databases e.g. PatientID. Only needed if different from the mapping table swap column name e.g. ECHI")]
+ public string? LiveDatabaseFieldName { get; set; }
- [Option('q', "Qualifier", HelpText = "Qualifier for values e.g. '. This should be the DBMS qualifier needed for strings/dates. If patient identifiers are numerical then do not specify this option")]
- public char? Qualifier { get; set; }
- }
+ [Option('q', "Qualifier", HelpText = "Qualifier for values e.g. '. This should be the DBMS qualifier needed for strings/dates. If patient identifiers are numerical then do not specify this option")]
+ public char? Qualifier { get; set; }
}
diff --git a/src/SmiServices/Applications/TriggerUpdates/TriggerUpdatesHost.cs b/src/SmiServices/Applications/TriggerUpdates/TriggerUpdatesHost.cs
index 95bfe035c..6c26be537 100644
--- a/src/SmiServices/Applications/TriggerUpdates/TriggerUpdatesHost.cs
+++ b/src/SmiServices/Applications/TriggerUpdates/TriggerUpdatesHost.cs
@@ -4,28 +4,27 @@
using SmiServices.Common.Options;
-namespace SmiServices.Applications.TriggerUpdates
+namespace SmiServices.Applications.TriggerUpdates;
+
+public class TriggerUpdatesHost : MicroserviceHost
{
- public class TriggerUpdatesHost : MicroserviceHost
+ private readonly ITriggerUpdatesSource _source;
+ private readonly IProducerModel _producer;
+
+ public TriggerUpdatesHost(GlobalOptions options, ITriggerUpdatesSource source, IMessageBroker? messageBroker = null)
+ : base(options, messageBroker)
{
- private readonly ITriggerUpdatesSource _source;
- private readonly IProducerModel _producer;
+ _source = source;
+ _producer = MessageBroker.SetupProducer(options.TriggerUpdatesOptions!, isBatch: false);
+ }
- public TriggerUpdatesHost(GlobalOptions options, ITriggerUpdatesSource source, IMessageBroker? messageBroker = null)
- : base(options, messageBroker)
+ public override void Start()
+ {
+ foreach (var upd in _source.GetUpdates())
{
- _source = source;
- _producer = MessageBroker.SetupProducer(options.TriggerUpdatesOptions!, isBatch: false);
+ _producer.SendMessage(upd, isInResponseTo: null, routingKey: null);
}
- public override void Start()
- {
- foreach (var upd in _source.GetUpdates())
- {
- _producer.SendMessage(upd, isInResponseTo: null, routingKey: null);
- }
-
- Stop("Update detection process finished");
- }
+ Stop("Update detection process finished");
}
}
diff --git a/src/SmiServices/Applications/TriggerUpdates/TriggerUpdatesOptions.cs b/src/SmiServices/Applications/TriggerUpdates/TriggerUpdatesOptions.cs
index 60ed433f4..0595363c3 100644
--- a/src/SmiServices/Applications/TriggerUpdates/TriggerUpdatesOptions.cs
+++ b/src/SmiServices/Applications/TriggerUpdates/TriggerUpdatesOptions.cs
@@ -1,10 +1,9 @@
using SmiServices.Common.Options;
-namespace SmiServices.Applications.TriggerUpdates
+namespace SmiServices.Applications.TriggerUpdates;
+
+public abstract class TriggerUpdatesCliOptions : CliOptions
{
- public abstract class TriggerUpdatesCliOptions : CliOptions
- {
- }
}
diff --git a/src/SmiServices/Common/Events/ControlEventHandler.cs b/src/SmiServices/Common/Events/ControlEventHandler.cs
index 070078f40..cd7149431 100644
--- a/src/SmiServices/Common/Events/ControlEventHandler.cs
+++ b/src/SmiServices/Common/Events/ControlEventHandler.cs
@@ -1,9 +1,8 @@
-namespace SmiServices.Common.Events
-{
- ///
- /// Event handler for hosts to implement if they wish to listen to specific control commands
- ///
- ///
- ///
- public delegate void ControlEventHandler(string routingKey, string? message = null);
-}
+namespace SmiServices.Common.Events;
+
+///
+/// Event handler for hosts to implement if they wish to listen to specific control commands
+///
+///
+///
+public delegate void ControlEventHandler(string routingKey, string? message = null);
diff --git a/src/SmiServices/Common/Events/FatalErrorEventArgs.cs b/src/SmiServices/Common/Events/FatalErrorEventArgs.cs
index d6d3d3f3a..a0e11270f 100644
--- a/src/SmiServices/Common/Events/FatalErrorEventArgs.cs
+++ b/src/SmiServices/Common/Events/FatalErrorEventArgs.cs
@@ -2,28 +2,27 @@
using RabbitMQ.Client.Events;
using System;
-namespace SmiServices.Common.Events
+namespace SmiServices.Common.Events;
+
+public class FatalErrorEventArgs : EventArgs
{
- public class FatalErrorEventArgs : EventArgs
- {
- public string Message { get; init; }
- public Exception? Exception { get; init; }
+ public string Message { get; init; }
+ public Exception? Exception { get; init; }
- public FatalErrorEventArgs(string msg, Exception exception)
- {
- Message = msg;
- Exception = exception;
- }
+ public FatalErrorEventArgs(string msg, Exception exception)
+ {
+ Message = msg;
+ Exception = exception;
+ }
- public FatalErrorEventArgs(BasicReturnEventArgs ra)
- {
- Message = $"BasicReturnEventArgs: {ra.ReplyCode} - {ra.ReplyText}. (Exchange: {ra.Exchange}, RoutingKey: {ra.RoutingKey})";
- }
+ public FatalErrorEventArgs(BasicReturnEventArgs ra)
+ {
+ Message = $"BasicReturnEventArgs: {ra.ReplyCode} - {ra.ReplyText}. (Exchange: {ra.Exchange}, RoutingKey: {ra.RoutingKey})";
+ }
- public override string ToString()
- {
- return $"{base.ToString()}, Message={Message}, Exception={Exception}, ";
- }
+ public override string ToString()
+ {
+ return $"{base.ToString()}, Message={Message}, Exception={Exception}, ";
}
}
diff --git a/src/SmiServices/Common/Events/FatalErrorHandlers.cs b/src/SmiServices/Common/Events/FatalErrorHandlers.cs
index 5fc830d8a..c5991d493 100644
--- a/src/SmiServices/Common/Events/FatalErrorHandlers.cs
+++ b/src/SmiServices/Common/Events/FatalErrorHandlers.cs
@@ -1,11 +1,10 @@
using RabbitMQ.Client.Events;
-namespace SmiServices.Common.Events
-{
- public delegate void HostFatalHandler(object sender, FatalErrorEventArgs e);
+namespace SmiServices.Common.Events;
- public delegate void ConsumerFatalHandler(object sender, FatalErrorEventArgs e);
+public delegate void HostFatalHandler(object sender, FatalErrorEventArgs e);
- public delegate void ProducerFatalHandler(object sender, BasicReturnEventArgs e);
-}
+public delegate void ConsumerFatalHandler(object sender, FatalErrorEventArgs e);
+
+public delegate void ProducerFatalHandler(object sender, BasicReturnEventArgs e);
diff --git a/src/SmiServices/Common/Events/StopEventHandler.cs b/src/SmiServices/Common/Events/StopEventHandler.cs
index 8d103f3a0..fa8b75e34 100644
--- a/src/SmiServices/Common/Events/StopEventHandler.cs
+++ b/src/SmiServices/Common/Events/StopEventHandler.cs
@@ -1,4 +1,3 @@
-namespace SmiServices.Common.Events
-{
- public delegate void StopEventHandler();
-}
+namespace SmiServices.Common.Events;
+
+public delegate void StopEventHandler();
diff --git a/src/SmiServices/Common/Execution/IMicroserviceHost.cs b/src/SmiServices/Common/Execution/IMicroserviceHost.cs
index 4a8364fbc..e19f63612 100644
--- a/src/SmiServices/Common/Execution/IMicroserviceHost.cs
+++ b/src/SmiServices/Common/Execution/IMicroserviceHost.cs
@@ -1,12 +1,11 @@
using SmiServices.Common.Events;
-namespace SmiServices.Common.Execution
+namespace SmiServices.Common.Execution;
+
+public interface IMicroserviceHost
{
- public interface IMicroserviceHost
- {
- ///
- ///
- ///
- event HostFatalHandler OnFatal;
- }
+ ///
+ ///
+ ///
+ event HostFatalHandler OnFatal;
}
diff --git a/src/SmiServices/Common/Execution/MicroserviceHost.cs b/src/SmiServices/Common/Execution/MicroserviceHost.cs
index 26a6b7c7f..eb0a9c20e 100644
--- a/src/SmiServices/Common/Execution/MicroserviceHost.cs
+++ b/src/SmiServices/Common/Execution/MicroserviceHost.cs
@@ -8,185 +8,184 @@
using SmiServices.Common.Options;
using System;
-namespace SmiServices.Common.Execution
-{
- public abstract class MicroserviceHost : IMicroserviceHost
- {
- public event HostFatalHandler OnFatal;
-
- protected readonly string HostProcessName;
- protected readonly int HostProcessID;
+namespace SmiServices.Common.Execution;
- protected readonly GlobalOptions Globals;
- protected readonly ILogger Logger;
+public abstract class MicroserviceHost : IMicroserviceHost
+{
+ public event HostFatalHandler OnFatal;
- protected readonly IMessageBroker MessageBroker;
+ protected readonly string HostProcessName;
+ protected readonly int HostProcessID;
+ protected readonly GlobalOptions Globals;
+ protected readonly ILogger Logger;
- private readonly object _oAdapterLock = new();
- private bool _auxConnectionsCreated;
+ protected readonly IMessageBroker MessageBroker;
- private readonly ProducerOptions _fatalLoggingProducerOptions;
- private IProducerModel? _fatalLoggingProducer;
- private readonly ControlMessageConsumer _controlMessageConsumer = null!;
+ private readonly object _oAdapterLock = new();
+ private bool _auxConnectionsCreated;
- private bool _stopCalled;
+ private readonly ProducerOptions _fatalLoggingProducerOptions;
+ private IProducerModel? _fatalLoggingProducer;
- protected readonly MicroserviceObjectFactory ObjectFactory;
+ private readonly ControlMessageConsumer _controlMessageConsumer = null!;
- ///
- /// Loads logging, sets up fatal behaviour, subscribes rabbit etc.
- ///
- /// Settings for the microservice (location of rabbit, queue names etc)
- ///
- protected MicroserviceHost(
- GlobalOptions globals,
- IMessageBroker? messageBroker = null)
- {
- if (globals == null || globals.FileSystemOptions == null || globals.RabbitOptions == null || globals.LoggingOptions == null)
- throw new ArgumentException("All or part of the global options are null");
+ private bool _stopCalled;
- // Disable fo-dicom's DICOM validation globally from here
- new DicomSetupBuilder().SkipValidation();
+ protected readonly MicroserviceObjectFactory ObjectFactory;
- HostProcessName = globals.HostProcessName;
+ ///
+ /// Loads logging, sets up fatal behaviour, subscribes rabbit etc.
+ ///
+ /// Settings for the microservice (location of rabbit, queue names etc)
+ ///
+ protected MicroserviceHost(
+ GlobalOptions globals,
+ IMessageBroker? messageBroker = null)
+ {
+ if (globals == null || globals.FileSystemOptions == null || globals.RabbitOptions == null || globals.LoggingOptions == null)
+ throw new ArgumentException("All or part of the global options are null");
- Logger = LogManager.GetLogger(GetType().Name);
- Logger.Info("Host logger created");
+ // Disable fo-dicom's DICOM validation globally from here
+ new DicomSetupBuilder().SkipValidation();
- HostProcessID = Environment.ProcessId;
- Logger.Info($"Starting {HostProcessName} (Host={Environment.MachineName} PID={HostProcessID} User={Environment.UserName})");
+ HostProcessName = globals.HostProcessName;
- // log centrally
- Globals = globals;
- Logger.Debug($"Loaded global options:\n{globals}");
+ Logger = LogManager.GetLogger(GetType().Name);
+ Logger.Info("Host logger created");
- // should also be centralized for non-host uses
- // Ensure this is false in case the default changes
- DicomTypeTranslater.SerializeBinaryData = false;
+ HostProcessID = Environment.ProcessId;
+ Logger.Info($"Starting {HostProcessName} (Host={Environment.MachineName} PID={HostProcessID} User={Environment.UserName})");
- _fatalLoggingProducerOptions = new ProducerOptions
- {
- ExchangeName = Globals.RabbitOptions.FatalLoggingExchange
- };
+ // log centrally
+ Globals = globals;
+ Logger.Debug($"Loaded global options:\n{globals}");
- //TODO This won't pass for testing with mocked filesystems
- //if(!Directory.Exists(options.FileSystemRoot))
- // throw new ArgumentException("Could not locate the FileSystemRoot \"" + options.FileSystemRoot + "\"");
+ // should also be centralized for non-host uses
+ // Ensure this is false in case the default changes
+ DicomTypeTranslater.SerializeBinaryData = false;
- OnFatal += (sender, args) => Fatal(args.Message, args.Exception);
+ _fatalLoggingProducerOptions = new ProducerOptions
+ {
+ ExchangeName = Globals.RabbitOptions.FatalLoggingExchange
+ };
- if (messageBroker == null)
- {
- messageBroker = new RabbitMQBroker(globals.RabbitOptions, HostProcessName + HostProcessID, OnFatal);
- var controlExchangeName = globals.RabbitOptions.RabbitMqControlExchangeName ?? throw new ArgumentNullException(nameof(globals));
- _controlMessageConsumer = new ControlMessageConsumer(globals.RabbitOptions, HostProcessName, HostProcessID, controlExchangeName, Stop);
- }
- MessageBroker = messageBroker;
+ //TODO This won't pass for testing with mocked filesystems
+ //if(!Directory.Exists(options.FileSystemRoot))
+ // throw new ArgumentException("Could not locate the FileSystemRoot \"" + options.FileSystemRoot + "\"");
- ObjectFactory = new MicroserviceObjectFactory
- {
- FatalHandler = (s, e) => Fatal(e.Message, e.Exception)
- };
- }
+ OnFatal += (sender, args) => Fatal(args.Message, args.Exception);
- ///
- /// Add an event handler to the control message consumer
- ///
- /// Method to call when invoked. Parameters are the action to perform, and the message body
- protected void AddControlHandler(IControlMessageHandler handler)
+ if (messageBroker == null)
{
- //(a, m) => action, message content
- _controlMessageConsumer.ControlEvent += handler.ControlMessageHandler;
+ messageBroker = new RabbitMQBroker(globals.RabbitOptions, HostProcessName + HostProcessID, OnFatal);
+ var controlExchangeName = globals.RabbitOptions.RabbitMqControlExchangeName ?? throw new ArgumentNullException(nameof(globals));
+ _controlMessageConsumer = new ControlMessageConsumer(globals.RabbitOptions, HostProcessName, HostProcessID, controlExchangeName, Stop);
}
+ MessageBroker = messageBroker;
- ///
- /// Start this separately so we don't block the thread if the host constructor throws an exception
- ///
- public void StartAuxConnections()
+ ObjectFactory = new MicroserviceObjectFactory
{
- lock (_oAdapterLock)
- {
- if (_auxConnectionsCreated)
- return;
-
- _auxConnectionsCreated = true;
+ FatalHandler = (s, e) => Fatal(e.Message, e.Exception)
+ };
+ }
- // Ensures no consumers have been started until we explicitly call Start()
- if (MessageBroker.HasConsumers)
- throw new ApplicationException("Rabbit adapter has consumers before aux. connections created");
+ ///
+ /// Add an event handler to the control message consumer
+ ///
+ /// Method to call when invoked. Parameters are the action to perform, and the message body
+ protected void AddControlHandler(IControlMessageHandler handler)
+ {
+ //(a, m) => action, message content
+ _controlMessageConsumer.ControlEvent += handler.ControlMessageHandler;
+ }
- _fatalLoggingProducer = MessageBroker.SetupProducer(_fatalLoggingProducerOptions, isBatch: false);
- MessageBroker.StartControlConsumer(_controlMessageConsumer);
- }
- }
+ ///
+ /// Start this separately so we don't block the thread if the host constructor throws an exception
+ ///
+ public void StartAuxConnections()
+ {
+ lock (_oAdapterLock)
+ {
+ if (_auxConnectionsCreated)
+ return;
- ///
- /// Per-host implementation. objects should not be started outside this method
- ///
- public abstract void Start();
+ _auxConnectionsCreated = true;
- //TODO Expose timeout here
- public virtual void Stop(string reason)
- {
- Logger.Info($"Host Stop called: {reason}");
+ // Ensures no consumers have been started until we explicitly call Start()
+ if (MessageBroker.HasConsumers)
+ throw new ApplicationException("Rabbit adapter has consumers before aux. connections created");
- if (_stopCalled)
- Logger.Warn("Host stop called twice");
+ _fatalLoggingProducer = MessageBroker.SetupProducer(_fatalLoggingProducerOptions, isBatch: false);
+ MessageBroker.StartControlConsumer(_controlMessageConsumer);
+ }
+ }
- _stopCalled = true;
+ ///
+ /// Per-host implementation. objects should not be started outside this method
+ ///
+ public abstract void Start();
- Logger.Debug("Shutting down RabbitMQ connections");
+ //TODO Expose timeout here
+ public virtual void Stop(string reason)
+ {
+ Logger.Info($"Host Stop called: {reason}");
- // Attempt to destroy the control queue
+ if (_stopCalled)
+ Logger.Warn("Host stop called twice");
- try
- {
- _controlMessageConsumer.Shutdown();
- }
- catch (Exception e)
- {
- Logger.Warn($"Could not clean up control queues: {e.Message}");
- }
+ _stopCalled = true;
- lock (_oAdapterLock)
- {
- MessageBroker.Shutdown(RabbitMQBroker.DefaultOperationTimeout);
- }
+ Logger.Debug("Shutting down RabbitMQ connections");
- Logger.Info("Host stop completed");
+ // Attempt to destroy the control queue
- // Always remember to flush!
- LogManager.Shutdown();
+ try
+ {
+ _controlMessageConsumer.Shutdown();
+ }
+ catch (Exception e)
+ {
+ Logger.Warn($"Could not clean up control queues: {e.Message}");
}
- ///
- /// Fatal essentially just calls , but attempts to send a FatalErrorMessage to RabbitMQ first
- ///
- ///
- ///
- public void Fatal(string msg, Exception? exception)
+ lock (_oAdapterLock)
{
- Logger.Fatal(exception, msg);
- if (_stopCalled)
- return;
+ MessageBroker.Shutdown(RabbitMQBroker.DefaultOperationTimeout);
+ }
- try
- {
- _fatalLoggingProducer?.SendMessage(new FatalErrorMessage(msg, exception), null, null);
- }
- catch (Exception e)
- {
- Logger.Error(e, "Failed to log fatal error");
- }
+ Logger.Info("Host stop completed");
- Stop($"Fatal error in MicroserviceHost ({msg})");
- }
+ // Always remember to flush!
+ LogManager.Shutdown();
+ }
- public void Wait()
+ ///
+ /// Fatal essentially just calls , but attempts to send a FatalErrorMessage to RabbitMQ first
+ ///
+ ///
+ ///
+ public void Fatal(string msg, Exception? exception)
+ {
+ Logger.Fatal(exception, msg);
+ if (_stopCalled)
+ return;
+
+ try
+ {
+ _fatalLoggingProducer?.SendMessage(new FatalErrorMessage(msg, exception), null, null);
+ }
+ catch (Exception e)
{
- MessageBroker.Wait();
+ Logger.Error(e, "Failed to log fatal error");
}
+
+ Stop($"Fatal error in MicroserviceHost ({msg})");
+ }
+
+ public void Wait()
+ {
+ MessageBroker.Wait();
}
}
diff --git a/src/SmiServices/Common/Execution/MicroserviceHostBootstrapper.cs b/src/SmiServices/Common/Execution/MicroserviceHostBootstrapper.cs
index df1521ca8..ef66b7403 100644
--- a/src/SmiServices/Common/Execution/MicroserviceHostBootstrapper.cs
+++ b/src/SmiServices/Common/Execution/MicroserviceHostBootstrapper.cs
@@ -1,76 +1,75 @@
using System;
-namespace SmiServices.Common.Execution
+namespace SmiServices.Common.Execution;
+
+///
+/// Wraps construction and startup of your applications MicroserviceHost. Handles Exceptions thrown during construction / setup as well as Ctrl+C support in standardised way
+///
+public class MicroserviceHostBootstrapper
{
+ private readonly Func _func;
+
+
///
- /// Wraps construction and startup of your applications MicroserviceHost. Handles Exceptions thrown during construction / setup as well as Ctrl+C support in standardised way
+ /// Default constructor
///
- public class MicroserviceHostBootstrapper
+ /// Construct with your host constructor call and then 'return bootStrapper.Main();'
+ public MicroserviceHostBootstrapper(Func func)
{
- private readonly Func _func;
+ _func = func;
+ }
+ public int Main()
+ {
+ Console.WriteLine("Bootstrapper -> Main called, constructing host");
- ///
- /// Default constructor
- ///
- /// Construct with your host constructor call and then 'return bootStrapper.Main();'
- public MicroserviceHostBootstrapper(Func func)
+ // Set up a periodic forced GC to avoid wasting RAM on multi-service hosts:
+ new System.Timers.Timer { Interval = 3600000, AutoReset = true, Enabled = true }.Elapsed += // lgtm[cs/local-not-disposed]
+ (o, args) =>
+ {
+ System.Runtime.GCSettings.LargeObjectHeapCompactionMode = System.Runtime.GCLargeObjectHeapCompactionMode.CompactOnce;
+ GC.Collect(2, GCCollectionMode.Forced, true, true);
+ };
+
+ MicroserviceHost host;
+
+ try
{
- _func = func;
+ host = _func();
}
-
- public int Main()
+ catch (Exception e)
{
- Console.WriteLine("Bootstrapper -> Main called, constructing host");
-
- // Set up a periodic forced GC to avoid wasting RAM on multi-service hosts:
- new System.Timers.Timer { Interval = 3600000, AutoReset = true, Enabled = true }.Elapsed += // lgtm[cs/local-not-disposed]
- (o, args) =>
- {
- System.Runtime.GCSettings.LargeObjectHeapCompactionMode = System.Runtime.GCLargeObjectHeapCompactionMode.CompactOnce;
- GC.Collect(2, GCCollectionMode.Forced, true, true);
- };
-
- MicroserviceHost host;
-
- try
- {
- host = _func();
- }
- catch (Exception e)
- {
- string nl = Environment.NewLine;
- Console.Error.WriteLine($"{e}{nl}{nl}Host constructor threw an exception:{nl}{e.Message}");
- return -1;
- }
+ string nl = Environment.NewLine;
+ Console.Error.WriteLine($"{e}{nl}{nl}Host constructor threw an exception:{nl}{e.Message}");
+ return -1;
+ }
- Console.WriteLine("Bootstrapper -> Host constructed, starting aux connections");
+ Console.WriteLine("Bootstrapper -> Host constructed, starting aux connections");
- Console.CancelKeyPress += delegate (object? _, ConsoleCancelEventArgs e)
- {
- e.Cancel = true;
- host.Stop("Ctrl+C pressed");
- };
+ Console.CancelKeyPress += delegate (object? _, ConsoleCancelEventArgs e)
+ {
+ e.Cancel = true;
+ host.Stop("Ctrl+C pressed");
+ };
- try
- {
- host.StartAuxConnections();
- Console.WriteLine("Bootstrapper -> Host aux connections started, calling Start()");
+ try
+ {
+ host.StartAuxConnections();
+ Console.WriteLine("Bootstrapper -> Host aux connections started, calling Start()");
- host.Start();
- Console.WriteLine("Bootstrapper -> Host created and started...");
- }
- catch (Exception e)
- {
- host.Fatal("Failed to start host", e);
- return -2;
- }
+ host.Start();
+ Console.WriteLine("Bootstrapper -> Host created and started...");
+ }
+ catch (Exception e)
+ {
+ host.Fatal("Failed to start host", e);
+ return -2;
+ }
- // Wait until Rabbit tasks are finished:
+ // Wait until Rabbit tasks are finished:
- host.Wait();
- Console.WriteLine("MicroserviceHostBootstrapper exiting. Service will exit when consumer threads are joined");
- return 0;
- }
+ host.Wait();
+ Console.WriteLine("MicroserviceHostBootstrapper exiting. Service will exit when consumer threads are joined");
+ return 0;
}
}
diff --git a/src/SmiServices/Common/FansiImplementations.cs b/src/SmiServices/Common/FansiImplementations.cs
index 23636b23b..fdd53a775 100644
--- a/src/SmiServices/Common/FansiImplementations.cs
+++ b/src/SmiServices/Common/FansiImplementations.cs
@@ -4,15 +4,14 @@
using FAnsi.Implementations.PostgreSql;
-namespace SmiServices.Common
+namespace SmiServices.Common;
+
+public static class FansiImplementations
{
- public static class FansiImplementations
+ public static void Load()
{
- public static void Load()
- {
- ImplementationManager.Load();
- ImplementationManager.Load();
- ImplementationManager.Load();
- }
+ ImplementationManager.Load();
+ ImplementationManager.Load();
+ ImplementationManager.Load();
}
}
diff --git a/src/SmiServices/Common/Helpers/DateTimeProvider.cs b/src/SmiServices/Common/Helpers/DateTimeProvider.cs
index 218b8995a..5dd7b94fb 100644
--- a/src/SmiServices/Common/Helpers/DateTimeProvider.cs
+++ b/src/SmiServices/Common/Helpers/DateTimeProvider.cs
@@ -1,10 +1,9 @@
using System;
-namespace SmiServices.Common.Helpers
+namespace SmiServices.Common.Helpers;
+
+public class DateTimeProvider
{
- public class DateTimeProvider
- {
- public virtual DateTime UtcNow() => DateTime.UtcNow;
- }
+ public virtual DateTime UtcNow() => DateTime.UtcNow;
}
diff --git a/src/SmiServices/Common/Helpers/IConsoleInput.cs b/src/SmiServices/Common/Helpers/IConsoleInput.cs
index 0ffd1a338..2f2fa2ca4 100644
--- a/src/SmiServices/Common/Helpers/IConsoleInput.cs
+++ b/src/SmiServices/Common/Helpers/IConsoleInput.cs
@@ -1,10 +1,9 @@
-namespace SmiServices.Common.Helpers
+namespace SmiServices.Common.Helpers;
+
+///
+/// Interface useful when testing interactive console input
+///
+public interface IConsoleInput
{
- ///
- /// Interface useful when testing interactive console input
- ///
- public interface IConsoleInput
- {
- public string? GetNextLine();
- }
+ public string? GetNextLine();
}
diff --git a/src/SmiServices/Common/Helpers/MicroserviceObjectFactory.cs b/src/SmiServices/Common/Helpers/MicroserviceObjectFactory.cs
index 7aefec30e..956a7053a 100644
--- a/src/SmiServices/Common/Helpers/MicroserviceObjectFactory.cs
+++ b/src/SmiServices/Common/Helpers/MicroserviceObjectFactory.cs
@@ -4,74 +4,73 @@
using System;
using System.Reflection;
-namespace SmiServices.Common.Helpers
-{
- public class MicroserviceObjectFactory
- {
- private readonly ILogger _logger = LogManager.GetCurrentClassLogger();
+namespace SmiServices.Common.Helpers;
- ///
- /// Method called when fails. If not set then the Exception is simply
- /// thrown.
- ///
- public HostFatalHandler? FatalHandler;
+public class MicroserviceObjectFactory
+{
+ private readonly ILogger _logger = LogManager.GetCurrentClassLogger();
- ///
- /// Constructs an instance of the specified and casts it to Type T (e.g. an interface). You can pass any
- /// required or optional objects required for invoking the class constructor in via .
- ///
- ///
- ///
- ///
- ///
- public T? CreateInstance(Type toCreate, params object[] optionalConstructorParameters)
- {
- T? toReturn = default;
+ ///
+ /// Method called when fails. If not set then the Exception is simply
+ /// thrown.
+ ///
+ public HostFatalHandler? FatalHandler;
- try
- {
- toReturn = (T)ObjectConstructor.ConstructIfPossible(toCreate, optionalConstructorParameters);
+ ///
+ /// Constructs an instance of the specified and casts it to Type T (e.g. an interface). You can pass any
+ /// required or optional objects required for invoking the class constructor in via .
+ ///
+ ///
+ ///
+ ///
+ ///
+ public T? CreateInstance(Type toCreate, params object[] optionalConstructorParameters)
+ {
+ T? toReturn = default;
- if (optionalConstructorParameters.Length > 0 && toReturn == null)
- toReturn = (T)ObjectConstructor.Construct(toCreate); // Try blank constructor
+ try
+ {
+ toReturn = (T)ObjectConstructor.ConstructIfPossible(toCreate, optionalConstructorParameters);
- if (toReturn == null)
- throw new Exception("ConstructIfPossible returned null");
+ if (optionalConstructorParameters.Length > 0 && toReturn == null)
+ toReturn = (T)ObjectConstructor.Construct(toCreate); // Try blank constructor
- _logger.Info($"Successfully constructed Type '{toReturn.GetType()}'");
- }
- catch (Exception e)
- {
- _logger.Error(e, $"Failed to construct Type '{typeof(T)}'");
+ if (toReturn == null)
+ throw new Exception("ConstructIfPossible returned null");
- if (FatalHandler != null)
- FatalHandler(this, new FatalErrorEventArgs($"Error constructing Type {toCreate}", e));
- else
- throw;
- }
+ _logger.Info($"Successfully constructed Type '{toReturn.GetType()}'");
+ }
+ catch (Exception e)
+ {
+ _logger.Error(e, $"Failed to construct Type '{typeof(T)}'");
- return toReturn;
+ if (FatalHandler != null)
+ FatalHandler(this, new FatalErrorEventArgs($"Error constructing Type {toCreate}", e));
+ else
+ throw;
}
- ///
- /// Constructs an instance of the specified in the specified Assembly and casts it to Type T (e.g. an interface).
- /// You can pass any required or optional objects required for invoking the class constructor in via .
- ///
- ///
- ///
- ///
- ///
- ///
- public T? CreateInstance(string typeName, Assembly assembly, params object[] optionalConstructorParameters)
- {
- if (string.IsNullOrWhiteSpace(typeName))
- {
- _logger.Warn($"No Type name specified for T {typeof(T).Name}");
- return default;
- }
+ return toReturn;
+ }
- Type toCreate = assembly.GetType(typeName, true) ?? throw new Exception($"Could not create type {typeName} from the given Assembly {assembly}");
- return CreateInstance(toCreate, optionalConstructorParameters);
+ ///
+ /// Constructs an instance of the specified in the specified Assembly and casts it to Type T (e.g. an interface).
+ /// You can pass any required or optional objects required for invoking the class constructor in via .
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ public T? CreateInstance(string typeName, Assembly assembly, params object[] optionalConstructorParameters)
+ {
+ if (string.IsNullOrWhiteSpace(typeName))
+ {
+ _logger.Warn($"No Type name specified for T {typeof(T).Name}");
+ return default;
}
+
+ Type toCreate = assembly.GetType(typeName, true) ?? throw new Exception($"Could not create type {typeName} from the given Assembly {assembly}");
+ return CreateInstance(toCreate, optionalConstructorParameters);
}
}
diff --git a/src/SmiServices/Common/Helpers/RealConsoleInput.cs b/src/SmiServices/Common/Helpers/RealConsoleInput.cs
index d0c11f952..6a63c6dcc 100644
--- a/src/SmiServices/Common/Helpers/RealConsoleInput.cs
+++ b/src/SmiServices/Common/Helpers/RealConsoleInput.cs
@@ -1,12 +1,11 @@
using System;
-namespace SmiServices.Common.Helpers
+namespace SmiServices.Common.Helpers;
+
+///
+/// Returns the next line from the console
+///
+public class RealConsoleInput : IConsoleInput
{
- ///
- /// Returns the next line from the console
- ///
- public class RealConsoleInput : IConsoleInput
- {
- public string? GetNextLine() => Console.ReadLine()?.Trim();
- }
+ public string? GetNextLine() => Console.ReadLine()?.Trim();
}
diff --git a/src/SmiServices/Common/IMessageBroker.cs b/src/SmiServices/Common/IMessageBroker.cs
index 2adf349f5..dbccdf196 100644
--- a/src/SmiServices/Common/IMessageBroker.cs
+++ b/src/SmiServices/Common/IMessageBroker.cs
@@ -4,26 +4,25 @@
using SmiServices.Common.Options;
using System;
-namespace SmiServices.Common
+namespace SmiServices.Common;
+
+public interface IMessageBroker
{
- public interface IMessageBroker
- {
- bool HasConsumers { get; }
+ bool HasConsumers { get; }
- Guid StartConsumer(ConsumerOptions consumerOptions, IConsumer consumer, bool isSolo) where T : IMessage;
+ Guid StartConsumer(ConsumerOptions consumerOptions, IConsumer consumer, bool isSolo) where T : IMessage;
- void StartControlConsumer(IControlMessageConsumer controlMessageConsumer);
+ void StartControlConsumer(IControlMessageConsumer controlMessageConsumer);
- void StopConsumer(Guid taskId, TimeSpan timeout);
+ void StopConsumer(Guid taskId, TimeSpan timeout);
- IProducerModel SetupProducer(ProducerOptions producerOptions, bool isBatch);
+ IProducerModel SetupProducer(ProducerOptions producerOptions, bool isBatch);
- IModel GetModel(string connectionName);
+ IModel GetModel(string connectionName);
- void Shutdown(TimeSpan timeout);
- public void Wait();
+ void Shutdown(TimeSpan timeout);
+ public void Wait();
- // Dreams of .NET Core 3.0...
- // void Shutdown() => Shutdown(TimeSpan.FromSeconds(5));
- }
+ // Dreams of .NET Core 3.0...
+ // void Shutdown() => Shutdown(TimeSpan.FromSeconds(5));
}
diff --git a/src/SmiServices/Common/MessageSerialization/JsonCompatibleDictionary.cs b/src/SmiServices/Common/MessageSerialization/JsonCompatibleDictionary.cs
index 65879644e..cdda40c01 100644
--- a/src/SmiServices/Common/MessageSerialization/JsonCompatibleDictionary.cs
+++ b/src/SmiServices/Common/MessageSerialization/JsonCompatibleDictionary.cs
@@ -3,60 +3,59 @@
using System.Collections.Generic;
using System.Linq;
-namespace SmiServices.Common.MessageSerialization
+namespace SmiServices.Common.MessageSerialization;
+
+///
+/// Allows Json serialization of complex key Types.
+///
+/// Out of the box Json serializes Dictionary keys using ToString and seems to ignore any custom JsonConverter specified on the key class. This class works around that behaviour
+/// by only serializing an array of keys and an array of values. Once both are populated then the underlying Dictionary Key/Values are created.
+///
+///
+///
+[JsonObject(MemberSerialization.OptIn)]
+public class JsonCompatibleDictionary : Dictionary where TK : notnull
{
- ///
- /// Allows Json serialization of complex key Types.
- ///
- /// Out of the box Json serializes Dictionary keys using ToString and seems to ignore any custom JsonConverter specified on the key class. This class works around that behaviour
- /// by only serializing an array of keys and an array of values. Once both are populated then the underlying Dictionary Key/Values are created.
- ///
- ///
- ///
- [JsonObject(MemberSerialization.OptIn)]
- public class JsonCompatibleDictionary : Dictionary where TK : notnull
+ [JsonProperty]
+ public TK[] SerializeableKeys
{
- [JsonProperty]
- public TK[] SerializeableKeys
- {
- get { return [.. Keys]; }
- set { Hydrate(value); }
- }
-
- [JsonProperty]
- public TV[] SerializeableValues
- {
- get { return [.. Values]; }
- set { Hydrate(value); }
- }
-
- private TK[]? _hydrateV1;
- private TV[]? _hydrateV2;
-
- private void Hydrate(TK[] value)
- {
- _hydrateV1 = value;
- Hydrate(_hydrateV1, _hydrateV2);
- }
-
- private void Hydrate(TV[]? value)
- {
- _hydrateV2 = value;
- Hydrate(_hydrateV1, _hydrateV2);
- }
-
- private void Hydrate(TK[]? hydrateV1, TV[]? hydrateV2)
- {
- if (hydrateV1 == null || hydrateV2 == null)
- return;
-
- if (_hydrateV1!.Length != hydrateV2.Length)
- return;
-
- Clear();
-
- for (int i = 0; i < _hydrateV1.Length; i++)
- Add(_hydrateV1[i], _hydrateV2![i]);
- }
+ get { return [.. Keys]; }
+ set { Hydrate(value); }
+ }
+
+ [JsonProperty]
+ public TV[] SerializeableValues
+ {
+ get { return [.. Values]; }
+ set { Hydrate(value); }
+ }
+
+ private TK[]? _hydrateV1;
+ private TV[]? _hydrateV2;
+
+ private void Hydrate(TK[] value)
+ {
+ _hydrateV1 = value;
+ Hydrate(_hydrateV1, _hydrateV2);
+ }
+
+ private void Hydrate(TV[]? value)
+ {
+ _hydrateV2 = value;
+ Hydrate(_hydrateV1, _hydrateV2);
+ }
+
+ private void Hydrate(TK[]? hydrateV1, TV[]? hydrateV2)
+ {
+ if (hydrateV1 == null || hydrateV2 == null)
+ return;
+
+ if (_hydrateV1!.Length != hydrateV2.Length)
+ return;
+
+ Clear();
+
+ for (int i = 0; i < _hydrateV1.Length; i++)
+ Add(_hydrateV1[i], _hydrateV2![i]);
}
}
diff --git a/src/SmiServices/Common/MessageSerialization/JsonConvert.cs b/src/SmiServices/Common/MessageSerialization/JsonConvert.cs
index 716090905..299b8a683 100644
--- a/src/SmiServices/Common/MessageSerialization/JsonConvert.cs
+++ b/src/SmiServices/Common/MessageSerialization/JsonConvert.cs
@@ -5,71 +5,70 @@
using System.Collections.Generic;
using System.Text;
-namespace SmiServices.Common.MessageSerialization
+namespace SmiServices.Common.MessageSerialization;
+
+///
+/// Helper class to (de)serialize objects from RabbitMQ messages.
+///
+public static class JsonConvert
{
- ///
- /// Helper class to (de)serialize objects from RabbitMQ messages.
- ///
- public static class JsonConvert
- {
- private static List _errors = [];
+ private static List _errors = [];
- private static readonly JsonSerializerSettings _serializerSettings = new()
+ private static readonly JsonSerializerSettings _serializerSettings = new()
+ {
+ Error = delegate (object? sender, ErrorEventArgs args)
{
- Error = delegate (object? sender, ErrorEventArgs args)
- {
- _errors.Add(args.ErrorContext.Error.Message);
- args.ErrorContext.Handled = true;
- },
- MissingMemberHandling = MissingMemberHandling.Error
- };
+ _errors.Add(args.ErrorContext.Error.Message);
+ args.ErrorContext.Handled = true;
+ },
+ MissingMemberHandling = MissingMemberHandling.Error
+ };
- ///
- /// Deserialize a message from a string.
- ///
- /// The type of to deserialize into.
- /// The message to deserialize.
- ///
- public static T DeserializeObject(string message) where T : IMessage
- {
- _errors = [];
+ ///
+ /// Deserialize a message from a string.
+ ///
+ /// The type of to deserialize into.
+ /// The message to deserialize.
+ ///
+ public static T DeserializeObject(string message) where T : IMessage
+ {
+ _errors = [];
- var messageObj = Newtonsoft.Json.JsonConvert.DeserializeObject(message, _serializerSettings)
- ?? throw new JsonSerializationException("Deserialized message object is null, message was empty.");
+ var messageObj = Newtonsoft.Json.JsonConvert.DeserializeObject(message, _serializerSettings)
+ ?? throw new JsonSerializationException("Deserialized message object is null, message was empty.");
- if (_errors.Count == 0)
- return messageObj;
+ if (_errors.Count == 0)
+ return messageObj;
- var e = new JsonSerializationException("Couldn't deserialize message to " + typeof(T).FullName + ". See exception data.");
+ var e = new JsonSerializationException("Couldn't deserialize message to " + typeof(T).FullName + ". See exception data.");
- for (var i = 0; i < _errors.Count; i++)
- e.Data.Add(i, _errors[i]);
+ for (var i = 0; i < _errors.Count; i++)
+ e.Data.Add(i, _errors[i]);
- throw e;
- }
+ throw e;
+ }
- ///
- /// Deserialize a message straight from the . Encoding defaults to UTF8 if not set.
- ///
- /// The type of to deserialize into.
- /// The message and all associated information.
- ///
- public static T DeserializeObject(BasicDeliverEventArgs deliverArgs) where T : IMessage
- {
- Encoding enc = Encoding.UTF8;
+ ///
+ /// Deserialize a message straight from the . Encoding defaults to UTF8 if not set.
+ ///
+ /// The type of to deserialize into.
+ /// The message and all associated information.
+ ///
+ public static T DeserializeObject(BasicDeliverEventArgs deliverArgs) where T : IMessage
+ {
+ Encoding enc = Encoding.UTF8;
- if (deliverArgs.BasicProperties != null && deliverArgs.BasicProperties.ContentEncoding != null)
- enc = Encoding.GetEncoding(deliverArgs.BasicProperties.ContentEncoding);
+ if (deliverArgs.BasicProperties != null && deliverArgs.BasicProperties.ContentEncoding != null)
+ enc = Encoding.GetEncoding(deliverArgs.BasicProperties.ContentEncoding);
- //TODO This might crash if for some reason we have invalid Unicode points
- return DeserializeObject(enc.GetString(deliverArgs.Body.Span));
- }
+ //TODO This might crash if for some reason we have invalid Unicode points
+ return DeserializeObject(enc.GetString(deliverArgs.Body.Span));
+ }
- public static T DeserializeObject(byte[] body) where T : IMessage
- {
- Encoding enc = Encoding.UTF8;
- return DeserializeObject(enc.GetString(body));
- }
+ public static T DeserializeObject(byte[] body) where T : IMessage
+ {
+ Encoding enc = Encoding.UTF8;
+ return DeserializeObject(enc.GetString(body));
}
}
diff --git a/src/SmiServices/Common/Messages/AccessionDirectoryMessage.cs b/src/SmiServices/Common/Messages/AccessionDirectoryMessage.cs
index e01876eee..45936bbd4 100644
--- a/src/SmiServices/Common/Messages/AccessionDirectoryMessage.cs
+++ b/src/SmiServices/Common/Messages/AccessionDirectoryMessage.cs
@@ -4,31 +4,30 @@
using System;
using System.IO;
-namespace SmiServices.Common.Messages
+namespace SmiServices.Common.Messages;
+
+///
+/// Object representing an accession directory message.
+///
+public sealed class AccessionDirectoryMessage : MemberwiseEquatable, IMessage
{
///
- /// Object representing an accession directory message.
+ /// Directory path relative to the root path.
///
- public sealed class AccessionDirectoryMessage : MemberwiseEquatable, IMessage
- {
- ///
- /// Directory path relative to the root path.
- ///
- [JsonProperty(Required = Required.Always)]
- public string DirectoryPath { get; set; } = null!;
+ [JsonProperty(Required = Required.Always)]
+ public string DirectoryPath { get; set; } = null!;
- public AccessionDirectoryMessage() { }
+ public AccessionDirectoryMessage() { }
- public AccessionDirectoryMessage(string root, DirectoryInfo directory)
- {
- if (!directory.FullName.StartsWith(root, StringComparison.CurrentCultureIgnoreCase))
- throw new Exception("Directory '" + directory + "' did not share a common root with the root '" + root + "'");
+ public AccessionDirectoryMessage(string root, DirectoryInfo directory)
+ {
+ if (!directory.FullName.StartsWith(root, StringComparison.CurrentCultureIgnoreCase))
+ throw new Exception("Directory '" + directory + "' did not share a common root with the root '" + root + "'");
- DirectoryPath = directory.FullName[root.Length..].TrimStart(Path.DirectorySeparatorChar);
- }
+ DirectoryPath = directory.FullName[root.Length..].TrimStart(Path.DirectorySeparatorChar);
+ }
- public string GetAbsolutePath(string rootPath) => Path.Combine(rootPath, DirectoryPath);
+ public string GetAbsolutePath(string rootPath) => Path.Combine(rootPath, DirectoryPath);
- public override string ToString() => $"AccessionDirectoryMessage[DirectoryPath={DirectoryPath}]";
- }
+ public override string ToString() => $"AccessionDirectoryMessage[DirectoryPath={DirectoryPath}]";
}
diff --git a/src/SmiServices/Common/Messages/DicomFileMessage.cs b/src/SmiServices/Common/Messages/DicomFileMessage.cs
index a500b6d5b..44b90613e 100644
--- a/src/SmiServices/Common/Messages/DicomFileMessage.cs
+++ b/src/SmiServices/Common/Messages/DicomFileMessage.cs
@@ -5,107 +5,106 @@
using System.IO;
using System.Text;
-namespace SmiServices.Common.Messages
+namespace SmiServices.Common.Messages;
+
+///
+///
+/// Object representing a dicom file message.
+/// https://github.com/HicServices/SMIPlugin/wiki/SMI-RabbitMQ-messages-and-queues#dicomfilemessage
+///
+public sealed class DicomFileMessage : MemberwiseEquatable, IFileReferenceMessage
{
- ///
///
- /// Object representing a dicom file message.
- /// https://github.com/HicServices/SMIPlugin/wiki/SMI-RabbitMQ-messages-and-queues#dicomfilemessage
+ /// File path relative to the root path.
///
- public sealed class DicomFileMessage : MemberwiseEquatable, IFileReferenceMessage
- {
- ///
- /// File path relative to the root path.
- ///
- [JsonProperty(Required = Required.Always)]
- public string DicomFilePath { get; set; } = null!;
-
- public long DicomFileSize { get; set; } = -1;
+ [JsonProperty(Required = Required.Always)]
+ public string DicomFilePath { get; set; } = null!;
- ///
- /// Dicom tag (0020,000D).
- ///
- [JsonProperty(Required = Required.Always)]
- public string StudyInstanceUID { get; set; } = null!;
+ public long DicomFileSize { get; set; } = -1;
- ///
- /// Dicom tag (0020,000E).
- ///
- [JsonProperty(Required = Required.Always)]
- public string SeriesInstanceUID { get; set; } = null!;
+ ///
+ /// Dicom tag (0020,000D).
+ ///
+ [JsonProperty(Required = Required.Always)]
+ public string StudyInstanceUID { get; set; } = null!;
- ///
- /// Dicom tag (0008,0018)
- ///
- [JsonProperty(Required = Required.Always)]
- public string SOPInstanceUID { get; set; } = null!;
+ ///
+ /// Dicom tag (0020,000E).
+ ///
+ [JsonProperty(Required = Required.Always)]
+ public string SeriesInstanceUID { get; set; } = null!;
- ///
- /// Key-value pairs of Dicom tags and their values.
- ///
- [JsonProperty(Required = Required.Always)]
- public string DicomDataset { get; set; } = null!;
+ ///
+ /// Dicom tag (0008,0018)
+ ///
+ [JsonProperty(Required = Required.Always)]
+ public string SOPInstanceUID { get; set; } = null!;
+ ///
+ /// Key-value pairs of Dicom tags and their values.
+ ///
+ [JsonProperty(Required = Required.Always)]
+ public string DicomDataset { get; set; } = null!;
- public DicomFileMessage() { }
- public DicomFileMessage(string root, FileInfo file)
- : this(root, file.FullName) { }
+ public DicomFileMessage() { }
- public DicomFileMessage(string root, string file)
- {
- if (!file.StartsWith(root, StringComparison.CurrentCultureIgnoreCase))
- throw new Exception("File '" + file + "' did not share a common root with the root '" + root + "'");
+ public DicomFileMessage(string root, FileInfo file)
+ : this(root, file.FullName) { }
- DicomFilePath = file[root.Length..].TrimStart(Path.DirectorySeparatorChar);
- }
+ public DicomFileMessage(string root, string file)
+ {
+ if (!file.StartsWith(root, StringComparison.CurrentCultureIgnoreCase))
+ throw new Exception("File '" + file + "' did not share a common root with the root '" + root + "'");
- public string GetAbsolutePath(string rootPath)
- {
- return Path.Combine(rootPath, DicomFilePath);
- }
+ DicomFilePath = file[root.Length..].TrimStart(Path.DirectorySeparatorChar);
+ }
- public bool Validate(string fileSystemRoot)
- {
- var absolutePath = GetAbsolutePath(fileSystemRoot);
+ public string GetAbsolutePath(string rootPath)
+ {
+ return Path.Combine(rootPath, DicomFilePath);
+ }
- if (string.IsNullOrWhiteSpace(absolutePath))
- return false;
+ public bool Validate(string fileSystemRoot)
+ {
+ var absolutePath = GetAbsolutePath(fileSystemRoot);
- try
- {
- var dir = new FileInfo(absolutePath);
+ if (string.IsNullOrWhiteSpace(absolutePath))
+ return false;
- //There file referenced must exist
- return dir.Exists;
- }
- catch (Exception)
- {
- return false;
- }
+ try
+ {
+ var dir = new FileInfo(absolutePath);
+ //There file referenced must exist
+ return dir.Exists;
}
-
- public bool VerifyPopulated()
+ catch (Exception)
{
- return !string.IsNullOrWhiteSpace(DicomFilePath) &&
- !string.IsNullOrWhiteSpace(StudyInstanceUID) &&
- !string.IsNullOrWhiteSpace(SeriesInstanceUID) &&
- !string.IsNullOrWhiteSpace(SOPInstanceUID) &&
- !string.IsNullOrWhiteSpace(DicomDataset);
+ return false;
}
- public override string ToString()
- {
- var sb = new StringBuilder();
+ }
- sb.AppendLine("DicomFilePath: " + DicomFilePath);
- sb.AppendLine("StudyInstanceUID: " + StudyInstanceUID);
- sb.AppendLine("SeriesInstanceUID: " + SeriesInstanceUID);
- sb.AppendLine("SOPInstanceUID: " + SOPInstanceUID);
- sb.AppendLine("=== DicomDataset ===\n" + DicomDataset + "\n====================");
+ public bool VerifyPopulated()
+ {
+ return !string.IsNullOrWhiteSpace(DicomFilePath) &&
+ !string.IsNullOrWhiteSpace(StudyInstanceUID) &&
+ !string.IsNullOrWhiteSpace(SeriesInstanceUID) &&
+ !string.IsNullOrWhiteSpace(SOPInstanceUID) &&
+ !string.IsNullOrWhiteSpace(DicomDataset);
+ }
- return sb.ToString();
- }
+ public override string ToString()
+ {
+ var sb = new StringBuilder();
+
+ sb.AppendLine("DicomFilePath: " + DicomFilePath);
+ sb.AppendLine("StudyInstanceUID: " + StudyInstanceUID);
+ sb.AppendLine("SeriesInstanceUID: " + SeriesInstanceUID);
+ sb.AppendLine("SOPInstanceUID: " + SOPInstanceUID);
+ sb.AppendLine("=== DicomDataset ===\n" + DicomDataset + "\n====================");
+
+ return sb.ToString();
}
}
diff --git a/src/SmiServices/Common/Messages/Extraction/ExtractFileCollectionInfoMessage.cs b/src/SmiServices/Common/Messages/Extraction/ExtractFileCollectionInfoMessage.cs
index 35b2b4b3b..f2e723457 100644
--- a/src/SmiServices/Common/Messages/Extraction/ExtractFileCollectionInfoMessage.cs
+++ b/src/SmiServices/Common/Messages/Extraction/ExtractFileCollectionInfoMessage.cs
@@ -3,48 +3,47 @@
using System;
using System.Collections.Generic;
-namespace SmiServices.Common.Messages.Extraction
+namespace SmiServices.Common.Messages.Extraction;
+
+///
+/// Describes all the sent for a single key Tag (e.g. SeriesInstanceUID) value provided by
+/// (i.e. a single entry in ).
+///
+public class ExtractFileCollectionInfoMessage : ExtractMessage
{
///
- /// Describes all the sent for a single key Tag (e.g. SeriesInstanceUID) value provided by
- /// (i.e. a single entry in ).
+ /// Contains the value of the tag which is being extracted
+ ///
+ [JsonProperty(Required = Required.Always)]
+ public string KeyValue { get; set; } = null!;
+
+ ///
+ /// Collection of all the messages sent out as the result of an (headers only) along with the file path extracted
///
- public class ExtractFileCollectionInfoMessage : ExtractMessage
+ [JsonProperty(Required = Required.Always)]
+ public JsonCompatibleDictionary ExtractFileMessagesDispatched { get; set; } = null!;
+
+ ///
+ /// All the reasons for message rejection and count of occurrences
+ ///
+ [JsonProperty(Required = Required.Default)]
+ public Dictionary RejectionReasons { get; set; } = new Dictionary(StringComparer.CurrentCultureIgnoreCase);
+
+
+ [JsonConstructor]
+ public ExtractFileCollectionInfoMessage()
+ {
+ ExtractFileMessagesDispatched = [];
+ }
+
+ public ExtractFileCollectionInfoMessage(ExtractionRequestMessage request)
+ : base(request)
+ {
+ ExtractFileMessagesDispatched = [];
+ }
+
+ public override string ToString()
{
- ///
- /// Contains the value of the tag which is being extracted
- ///
- [JsonProperty(Required = Required.Always)]
- public string KeyValue { get; set; } = null!;
-
- ///
- /// Collection of all the messages sent out as the result of an (headers only) along with the file path extracted
- ///
- [JsonProperty(Required = Required.Always)]
- public JsonCompatibleDictionary ExtractFileMessagesDispatched { get; set; } = null!;
-
- ///
- /// All the reasons for message rejection and count of occurrences
- ///
- [JsonProperty(Required = Required.Default)]
- public Dictionary RejectionReasons { get; set; } = new Dictionary(StringComparer.CurrentCultureIgnoreCase);
-
-
- [JsonConstructor]
- public ExtractFileCollectionInfoMessage()
- {
- ExtractFileMessagesDispatched = [];
- }
-
- public ExtractFileCollectionInfoMessage(ExtractionRequestMessage request)
- : base(request)
- {
- ExtractFileMessagesDispatched = [];
- }
-
- public override string ToString()
- {
- return base.ToString() + $",KeyValue={KeyValue},ExtractFileMessagesDispatched={ExtractFileMessagesDispatched.Count},RejectionReasons={RejectionReasons.Count},";
- }
+ return base.ToString() + $",KeyValue={KeyValue},ExtractFileMessagesDispatched={ExtractFileMessagesDispatched.Count},RejectionReasons={RejectionReasons.Count},";
}
}
diff --git a/src/SmiServices/Common/Messages/Extraction/ExtractFileMessage.cs b/src/SmiServices/Common/Messages/Extraction/ExtractFileMessage.cs
index 83f9e0b68..53ae7c9f0 100644
--- a/src/SmiServices/Common/Messages/Extraction/ExtractFileMessage.cs
+++ b/src/SmiServices/Common/Messages/Extraction/ExtractFileMessage.cs
@@ -1,31 +1,30 @@
using Newtonsoft.Json;
-namespace SmiServices.Common.Messages.Extraction
+namespace SmiServices.Common.Messages.Extraction;
+
+///
+/// Describes a single image which should be extracted and anonymised using the provided anonymisation script
+///
+public class ExtractFileMessage : ExtractMessage, IFileReferenceMessage
{
///
- /// Describes a single image which should be extracted and anonymised using the provided anonymisation script
+ /// The file path where the original dicom file can be found, relative to the FileSystemRoot
///
- public class ExtractFileMessage : ExtractMessage, IFileReferenceMessage
- {
- ///
- /// The file path where the original dicom file can be found, relative to the FileSystemRoot
- ///
- [JsonProperty(Required = Required.Always)]
- public string DicomFilePath { get; set; } = null!;
+ [JsonProperty(Required = Required.Always)]
+ public string DicomFilePath { get; set; } = null!;
- ///
- /// The subdirectory and dicom filename within the ExtractionDirectory to extract the identifiable image (specified by ) into. For example
- /// "Series132\1234-an.dcm"
- ///
- [JsonProperty(Required = Required.Always)]
- public string OutputPath { get; set; } = null!;
+ ///
+ /// The subdirectory and dicom filename within the ExtractionDirectory to extract the identifiable image (specified by ) into. For example
+ /// "Series132\1234-an.dcm"
+ ///
+ [JsonProperty(Required = Required.Always)]
+ public string OutputPath { get; set; } = null!;
- [JsonConstructor]
- public ExtractFileMessage() { }
+ [JsonConstructor]
+ public ExtractFileMessage() { }
- public ExtractFileMessage(ExtractionRequestMessage request)
- : base(request) { }
- }
+ public ExtractFileMessage(ExtractionRequestMessage request)
+ : base(request) { }
}
diff --git a/src/SmiServices/Common/Messages/Extraction/ExtractMessage.cs b/src/SmiServices/Common/Messages/Extraction/ExtractMessage.cs
index be037bd53..c7de2ee62 100644
--- a/src/SmiServices/Common/Messages/Extraction/ExtractMessage.cs
+++ b/src/SmiServices/Common/Messages/Extraction/ExtractMessage.cs
@@ -2,86 +2,85 @@
using Newtonsoft.Json;
using System;
-namespace SmiServices.Common.Messages.Extraction
+namespace SmiServices.Common.Messages.Extraction;
+
+///
+/// Base class for all messages relating to the extract process
+///
+public abstract class ExtractMessage : MemberwiseEquatable, IExtractMessage
{
- ///
- /// Base class for all messages relating to the extract process
- ///
- public abstract class ExtractMessage : MemberwiseEquatable, IExtractMessage
- {
- [JsonProperty(Required = Required.Always)]
- public Guid ExtractionJobIdentifier { get; set; }
+ [JsonProperty(Required = Required.Always)]
+ public Guid ExtractionJobIdentifier { get; set; }
- [JsonProperty(Required = Required.Always)]
- public string ProjectNumber { get; set; } = null!;
+ [JsonProperty(Required = Required.Always)]
+ public string ProjectNumber { get; set; } = null!;
- [JsonProperty(Required = Required.Always)]
- public string ExtractionDirectory { get; set; } = null!;
+ [JsonProperty(Required = Required.Always)]
+ public string ExtractionDirectory { get; set; } = null!;
- [JsonProperty(Required = Required.Always)]
- public string Modality { get; set; } = null!;
+ [JsonProperty(Required = Required.Always)]
+ public string Modality { get; set; } = null!;
- [JsonProperty(Required = Required.Always)]
- public DateTime JobSubmittedAt { get; set; }
+ [JsonProperty(Required = Required.Always)]
+ public DateTime JobSubmittedAt { get; set; }
- [JsonProperty(Required = Required.Always)]
- public bool IsIdentifiableExtraction { get; set; }
+ [JsonProperty(Required = Required.Always)]
+ public bool IsIdentifiableExtraction { get; set; }
- [JsonProperty(Required = Required.Always)]
- public bool IsNoFilterExtraction { get; set; }
+ [JsonProperty(Required = Required.Always)]
+ public bool IsNoFilterExtraction { get; set; }
- [JsonProperty(Required = Required.Always)]
- public bool IsPooledExtraction { get; set; }
+ [JsonProperty(Required = Required.Always)]
+ public bool IsPooledExtraction { get; set; }
- [JsonConstructor]
- protected ExtractMessage() { }
+ [JsonConstructor]
+ protected ExtractMessage() { }
- protected ExtractMessage(
- Guid extractionJobIdentifier,
- string projectNumber,
- string extractionDirectory,
- string modality,
- DateTime jobSubmittedAt,
- bool isIdentifiableExtraction,
- bool isNoFilterExtraction,
- bool isPooledExtraction
- )
- : this()
- {
- ExtractionJobIdentifier = extractionJobIdentifier;
- ProjectNumber = projectNumber;
- ExtractionDirectory = extractionDirectory;
- Modality = modality;
- JobSubmittedAt = jobSubmittedAt;
- IsIdentifiableExtraction = isIdentifiableExtraction;
- IsNoFilterExtraction = isNoFilterExtraction;
- IsPooledExtraction = isPooledExtraction;
- }
+ protected ExtractMessage(
+ Guid extractionJobIdentifier,
+ string projectNumber,
+ string extractionDirectory,
+ string modality,
+ DateTime jobSubmittedAt,
+ bool isIdentifiableExtraction,
+ bool isNoFilterExtraction,
+ bool isPooledExtraction
+ )
+ : this()
+ {
+ ExtractionJobIdentifier = extractionJobIdentifier;
+ ProjectNumber = projectNumber;
+ ExtractionDirectory = extractionDirectory;
+ Modality = modality;
+ JobSubmittedAt = jobSubmittedAt;
+ IsIdentifiableExtraction = isIdentifiableExtraction;
+ IsNoFilterExtraction = isNoFilterExtraction;
+ IsPooledExtraction = isPooledExtraction;
+ }
- protected ExtractMessage(IExtractMessage request)
- : this(
- request.ExtractionJobIdentifier,
- request.ProjectNumber,
- request.ExtractionDirectory,
- request.Modality,
- request.JobSubmittedAt,
- request.IsIdentifiableExtraction,
- request.IsNoFilterExtraction,
- request.IsPooledExtraction
- )
- { }
+ protected ExtractMessage(IExtractMessage request)
+ : this(
+ request.ExtractionJobIdentifier,
+ request.ProjectNumber,
+ request.ExtractionDirectory,
+ request.Modality,
+ request.JobSubmittedAt,
+ request.IsIdentifiableExtraction,
+ request.IsNoFilterExtraction,
+ request.IsPooledExtraction
+ )
+ { }
- public override string ToString() =>
- $"ExtractionJobIdentifier={ExtractionJobIdentifier}, " +
- $"ProjectNumber={ProjectNumber}, " +
- $"ExtractionDirectory={ExtractionDirectory}, " +
- $"Modality={Modality}, " +
- $"JobSubmittedAt={JobSubmittedAt:s}, " +
- $"IsIdentifiableExtraction={IsIdentifiableExtraction}, " +
- $"IsNoFilterExtraction={IsNoFilterExtraction}, " +
- $"IsPooledExtraction={IsPooledExtraction}, " +
- "";
- }
+ public override string ToString() =>
+ $"ExtractionJobIdentifier={ExtractionJobIdentifier}, " +
+ $"ProjectNumber={ProjectNumber}, " +
+ $"ExtractionDirectory={ExtractionDirectory}, " +
+ $"Modality={Modality}, " +
+ $"JobSubmittedAt={JobSubmittedAt:s}, " +
+ $"IsIdentifiableExtraction={IsIdentifiableExtraction}, " +
+ $"IsNoFilterExtraction={IsNoFilterExtraction}, " +
+ $"IsPooledExtraction={IsPooledExtraction}, " +
+ "";
}
diff --git a/src/SmiServices/Common/Messages/Extraction/ExtractedFileStatus.cs b/src/SmiServices/Common/Messages/Extraction/ExtractedFileStatus.cs
index 2f6672949..57a001808 100644
--- a/src/SmiServices/Common/Messages/Extraction/ExtractedFileStatus.cs
+++ b/src/SmiServices/Common/Messages/Extraction/ExtractedFileStatus.cs
@@ -1,30 +1,29 @@
-namespace SmiServices.Common.Messages.Extraction
+namespace SmiServices.Common.Messages.Extraction;
+
+public enum ExtractedFileStatus
{
- public enum ExtractedFileStatus
- {
- ///
- /// Unused placeholder value
- ///
- None = 0,
+ ///
+ /// Unused placeholder value
+ ///
+ None = 0,
- ///
- /// The file has been anonymised successfully
- ///
- Anonymised,
+ ///
+ /// The file has been anonymised successfully
+ ///
+ Anonymised,
- ///
- /// The file could not be anonymised and will not be retired
- ///
- ErrorWontRetry,
+ ///
+ /// The file could not be anonymised and will not be retired
+ ///
+ ErrorWontRetry,
- ///
- /// The source file could not be found under the given filesystem root
- ///
- FileMissing,
+ ///
+ /// The source file could not be found under the given filesystem root
+ ///
+ FileMissing,
- ///
- /// The source file was successfully copied to the destination
- ///
- Copied,
- }
+ ///
+ /// The source file was successfully copied to the destination
+ ///
+ Copied,
}
diff --git a/src/SmiServices/Common/Messages/Extraction/ExtractedFileStatusMessage.cs b/src/SmiServices/Common/Messages/Extraction/ExtractedFileStatusMessage.cs
index 9f8b214a4..9e3e8e159 100644
--- a/src/SmiServices/Common/Messages/Extraction/ExtractedFileStatusMessage.cs
+++ b/src/SmiServices/Common/Messages/Extraction/ExtractedFileStatusMessage.cs
@@ -1,53 +1,52 @@
using Newtonsoft.Json;
-namespace SmiServices.Common.Messages.Extraction
+namespace SmiServices.Common.Messages.Extraction;
+
+///
+/// Status message sent by services which extract files (CTP, FileCopier)
+///
+public class ExtractedFileStatusMessage : ExtractMessage, IFileReferenceMessage
{
///
- /// Status message sent by services which extract files (CTP, FileCopier)
+ /// Original file path
+ ///
+ [JsonProperty(Required = Required.Always)]
+ public string DicomFilePath { get; set; } = null!;
+
+ ///
+ /// The for this file
+ ///
+ [JsonProperty(Required = Required.Always)]
+ public ExtractedFileStatus Status { get; set; }
+
+ ///
+ /// Output file path, relative to the extraction directory. Only required if an output file has been produced
///
- public class ExtractedFileStatusMessage : ExtractMessage, IFileReferenceMessage
+ [JsonProperty(Required = Required.AllowNull)]
+ public string? OutputFilePath { get; set; }
+
+ ///
+ /// Message required if Status is not 0
+ ///
+ [JsonProperty(Required = Required.AllowNull)]
+ public string? StatusMessage { get; set; }
+
+
+ [JsonConstructor]
+ public ExtractedFileStatusMessage() { }
+
+ public ExtractedFileStatusMessage(ExtractFileMessage request)
+ : base(request)
{
- ///
- /// Original file path
- ///
- [JsonProperty(Required = Required.Always)]
- public string DicomFilePath { get; set; } = null!;
-
- ///
- /// The for this file
- ///
- [JsonProperty(Required = Required.Always)]
- public ExtractedFileStatus Status { get; set; }
-
- ///
- /// Output file path, relative to the extraction directory. Only required if an output file has been produced
- ///
- [JsonProperty(Required = Required.AllowNull)]
- public string? OutputFilePath { get; set; }
-
- ///
- /// Message required if Status is not 0
- ///
- [JsonProperty(Required = Required.AllowNull)]
- public string? StatusMessage { get; set; }
-
-
- [JsonConstructor]
- public ExtractedFileStatusMessage() { }
-
- public ExtractedFileStatusMessage(ExtractFileMessage request)
- : base(request)
- {
- DicomFilePath = request.DicomFilePath;
- OutputFilePath = request.OutputPath;
- }
-
- public override string ToString() =>
- $"{base.ToString()}," +
- $"DicomFilePath={DicomFilePath}," +
- $"ExtractedFileStatus={Status}," +
- $"OutputFilePath={OutputFilePath}," +
- $"StatusMessage={StatusMessage}," +
- "";
+ DicomFilePath = request.DicomFilePath;
+ OutputFilePath = request.OutputPath;
}
+
+ public override string ToString() =>
+ $"{base.ToString()}," +
+ $"DicomFilePath={DicomFilePath}," +
+ $"ExtractedFileStatus={Status}," +
+ $"OutputFilePath={OutputFilePath}," +
+ $"StatusMessage={StatusMessage}," +
+ "";
}
diff --git a/src/SmiServices/Common/Messages/Extraction/ExtractedFileVerificationMessage.cs b/src/SmiServices/Common/Messages/Extraction/ExtractedFileVerificationMessage.cs
index efbd40b5c..a26e4f7c8 100644
--- a/src/SmiServices/Common/Messages/Extraction/ExtractedFileVerificationMessage.cs
+++ b/src/SmiServices/Common/Messages/Extraction/ExtractedFileVerificationMessage.cs
@@ -1,36 +1,35 @@
using Newtonsoft.Json;
using System;
-namespace SmiServices.Common.Messages.Extraction
+namespace SmiServices.Common.Messages.Extraction;
+
+public class ExtractedFileVerificationMessage : ExtractMessage, IFileReferenceMessage
{
- public class ExtractedFileVerificationMessage : ExtractMessage, IFileReferenceMessage
- {
- [JsonProperty(Required = Required.Always)]
- public VerifiedFileStatus Status { get; set; }
+ [JsonProperty(Required = Required.Always)]
+ public VerifiedFileStatus Status { get; set; }
- [JsonProperty(Required = Required.Always)]
- public string Report { get; set; } = null!;
+ [JsonProperty(Required = Required.Always)]
+ public string Report { get; set; } = null!;
- ///
- /// The originally sourced origin (identifiable file path).
- ///
- [JsonProperty(Required = Required.Always)]
- public string DicomFilePath { get; set; } = null!;
+ ///
+ /// The originally sourced origin (identifiable file path).
+ ///
+ [JsonProperty(Required = Required.Always)]
+ public string DicomFilePath { get; set; } = null!;
- ///
- /// Output file path, relative to the extraction directory. Only required if an output file has been produced
- ///
- [JsonProperty(Required = Required.Always)]
- public string OutputFilePath { get; set; } = null!;
+ ///
+ /// Output file path, relative to the extraction directory. Only required if an output file has been produced
+ ///
+ [JsonProperty(Required = Required.Always)]
+ public string OutputFilePath { get; set; } = null!;
- [JsonConstructor]
- public ExtractedFileVerificationMessage() { }
+ [JsonConstructor]
+ public ExtractedFileVerificationMessage() { }
- public ExtractedFileVerificationMessage(ExtractedFileStatusMessage request)
- : base(request)
- {
- DicomFilePath = request.DicomFilePath;
- OutputFilePath = request.OutputFilePath ?? throw new ArgumentNullException(nameof(request));
- }
+ public ExtractedFileVerificationMessage(ExtractedFileStatusMessage request)
+ : base(request)
+ {
+ DicomFilePath = request.DicomFilePath;
+ OutputFilePath = request.OutputFilePath ?? throw new ArgumentNullException(nameof(request));
}
}
diff --git a/src/SmiServices/Common/Messages/Extraction/ExtractionKey.cs b/src/SmiServices/Common/Messages/Extraction/ExtractionKey.cs
index c19b83fb4..8d9a9688d 100644
--- a/src/SmiServices/Common/Messages/Extraction/ExtractionKey.cs
+++ b/src/SmiServices/Common/Messages/Extraction/ExtractionKey.cs
@@ -1,21 +1,20 @@
-namespace SmiServices.Common.Messages.Extraction
+namespace SmiServices.Common.Messages.Extraction;
+
+// ReSharper disable InconsistentNaming
+public enum ExtractionKey
{
- // ReSharper disable InconsistentNaming
- public enum ExtractionKey
- {
- ///
- /// Dicom Tag (0008,0018)
- ///
- SOPInstanceUID,
+ ///
+ /// Dicom Tag (0008,0018)
+ ///
+ SOPInstanceUID,
- ///
- /// Dicom Tag (0020,000E)
- ///
- SeriesInstanceUID,
+ ///
+ /// Dicom Tag (0020,000E)
+ ///
+ SeriesInstanceUID,
- ///
- /// Dicom Tag (0020,000D)
- ///
- StudyInstanceUID,
- }
+ ///
+ /// Dicom Tag (0020,000D)
+ ///
+ StudyInstanceUID,
}
diff --git a/src/SmiServices/Common/Messages/Extraction/ExtractionRequestInfoMessage.cs b/src/SmiServices/Common/Messages/Extraction/ExtractionRequestInfoMessage.cs
index 4a446096c..b2e02dd3e 100644
--- a/src/SmiServices/Common/Messages/Extraction/ExtractionRequestInfoMessage.cs
+++ b/src/SmiServices/Common/Messages/Extraction/ExtractionRequestInfoMessage.cs
@@ -1,25 +1,24 @@
using Newtonsoft.Json;
-namespace SmiServices.Common.Messages.Extraction
+namespace SmiServices.Common.Messages.Extraction;
+
+public class ExtractionRequestInfoMessage : ExtractMessage
{
- public class ExtractionRequestInfoMessage : ExtractMessage
- {
- [JsonProperty(Required = Required.Always)]
- public string KeyTag { get; set; } = null!;
+ [JsonProperty(Required = Required.Always)]
+ public string KeyTag { get; set; } = null!;
- [JsonProperty(Required = Required.Always)]
- public int KeyValueCount { get; set; }
+ [JsonProperty(Required = Required.Always)]
+ public int KeyValueCount { get; set; }
- [JsonProperty(Required = Required.Always)]
- public string UserName { get; set; } = null!;
+ [JsonProperty(Required = Required.Always)]
+ public string UserName { get; set; } = null!;
- [JsonConstructor]
- public ExtractionRequestInfoMessage() { }
+ [JsonConstructor]
+ public ExtractionRequestInfoMessage() { }
- public override string ToString()
- {
- return base.ToString() + $",KeyTag={KeyTag},KeyValueCount={KeyValueCount},UserName={UserName}";
- }
+ public override string ToString()
+ {
+ return base.ToString() + $",KeyTag={KeyTag},KeyValueCount={KeyValueCount},UserName={UserName}";
}
}
diff --git a/src/SmiServices/Common/Messages/Extraction/ExtractionRequestMessage.cs b/src/SmiServices/Common/Messages/Extraction/ExtractionRequestMessage.cs
index 21074fa3f..9f9d881db 100644
--- a/src/SmiServices/Common/Messages/Extraction/ExtractionRequestMessage.cs
+++ b/src/SmiServices/Common/Messages/Extraction/ExtractionRequestMessage.cs
@@ -1,45 +1,44 @@
using Newtonsoft.Json;
using System.Collections.Generic;
-namespace SmiServices.Common.Messages.Extraction
+namespace SmiServices.Common.Messages.Extraction;
+
+///
+/// Describes a request to extract all images identified by a DicomTag e.g. SeriesInstanceUID with the specified project specific patient identifiers (PatientID)
+///
+public class ExtractionRequestMessage : ExtractMessage
{
///
- /// Describes a request to extract all images identified by a DicomTag e.g. SeriesInstanceUID with the specified project specific patient identifiers (PatientID)
+ /// Contains the name of the identifier you want to extract based on (this should be a DicomTag e.g. 'SeriesInstanceUID')
///
- public class ExtractionRequestMessage : ExtractMessage
- {
- ///
- /// Contains the name of the identifier you want to extract based on (this should be a DicomTag e.g. 'SeriesInstanceUID')
- ///
- [JsonProperty(Required = Required.Always)]
- public string KeyTag { get; set; } = null!;
-
- ///
- /// The unique set of identifiers of Type which should be extracted
- ///
- [JsonProperty(Required = Required.Always)]
- public List ExtractionIdentifiers { get; set; } = null!;
+ [JsonProperty(Required = Required.Always)]
+ public string KeyTag { get; set; } = null!;
- [JsonConstructor]
- public ExtractionRequestMessage()
- {
- ExtractionIdentifiers = [];
- }
+ ///
+ /// The unique set of identifiers of Type which should be extracted
+ ///
+ [JsonProperty(Required = Required.Always)]
+ public List ExtractionIdentifiers { get; set; } = null!;
- ///
- /// (Shallow) copy constructor
- ///
- ///
- public ExtractionRequestMessage(ExtractionRequestMessage other)
- : base(other)
- {
- KeyTag = other.KeyTag;
- ExtractionIdentifiers = other.ExtractionIdentifiers;
- }
+ [JsonConstructor]
+ public ExtractionRequestMessage()
+ {
+ ExtractionIdentifiers = [];
+ }
- public override string ToString()
- => base.ToString() + ", " +
- $"KeyTag={KeyTag}, " +
- $"nIdentifiers={ExtractionIdentifiers.Count}";
+ ///
+ /// (Shallow) copy constructor
+ ///
+ ///
+ public ExtractionRequestMessage(ExtractionRequestMessage other)
+ : base(other)
+ {
+ KeyTag = other.KeyTag;
+ ExtractionIdentifiers = other.ExtractionIdentifiers;
}
+
+ public override string ToString()
+ => base.ToString() + ", " +
+ $"KeyTag={KeyTag}, " +
+ $"nIdentifiers={ExtractionIdentifiers.Count}";
}
diff --git a/src/SmiServices/Common/Messages/Extraction/IExtractMessage.cs b/src/SmiServices/Common/Messages/Extraction/IExtractMessage.cs
index d646faf1c..028e21a2c 100644
--- a/src/SmiServices/Common/Messages/Extraction/IExtractMessage.cs
+++ b/src/SmiServices/Common/Messages/Extraction/IExtractMessage.cs
@@ -1,51 +1,50 @@
using Newtonsoft.Json;
using System;
-namespace SmiServices.Common.Messages.Extraction
+namespace SmiServices.Common.Messages.Extraction;
+
+///
+/// Interface for all messages relating to the extract process
+///
+public interface IExtractMessage : IMessage
{
///
- /// Interface for all messages relating to the extract process
- ///
- public interface IExtractMessage : IMessage
- {
- ///
- /// Unique identifier to link messages from different extract requests
- ///
- Guid ExtractionJobIdentifier { get; }
-
- ///
- /// Project number used by eDRIS for reference, and for the base extraction output relative to the ExtractRoot
- ///
- string ProjectNumber { get; }
-
- ///
- /// Directory relative to the ExtractRoot to place anonymised files into
- ///
- string ExtractionDirectory { get; }
-
- ///
- /// The modality to extract
- ///
- public string Modality { get; }
-
- ///
- /// DateTime the job was submitted at
- ///
- DateTime JobSubmittedAt { get; set; }
-
- ///
- /// True if this is an identifiable extraction (i.e. files should not be anonymised)
- ///
- bool IsIdentifiableExtraction { get; }
-
- ///
- /// True if this is a "no filters" (i.e. no file rejection filters should be applied)
- ///
- bool IsNoFilterExtraction { get; }
-
- ///
- /// True if this extraction uses the global pool of DICOM files
- ///
- bool IsPooledExtraction { get; }
- }
+ /// Unique identifier to link messages from different extract requests
+ ///
+ Guid ExtractionJobIdentifier { get; }
+
+ ///
+ /// Project number used by eDRIS for reference, and for the base extraction output relative to the ExtractRoot
+ ///
+ string ProjectNumber { get; }
+
+ ///
+ /// Directory relative to the ExtractRoot to place anonymised files into
+ ///
+ string ExtractionDirectory { get; }
+
+ ///
+ /// The modality to extract
+ ///
+ public string Modality { get; }
+
+ ///
+ /// DateTime the job was submitted at
+ ///
+ DateTime JobSubmittedAt { get; set; }
+
+ ///
+ /// True if this is an identifiable extraction (i.e. files should not be anonymised)
+ ///
+ bool IsIdentifiableExtraction { get; }
+
+ ///
+ /// True if this is a "no filters" (i.e. no file rejection filters should be applied)
+ ///
+ bool IsNoFilterExtraction { get; }
+
+ ///
+ /// True if this extraction uses the global pool of DICOM files
+ ///
+ bool IsPooledExtraction { get; }
}
diff --git a/src/SmiServices/Common/Messages/Extraction/VerifiedFileStatus.cs b/src/SmiServices/Common/Messages/Extraction/VerifiedFileStatus.cs
index 81118ac71..af9a70d3c 100644
--- a/src/SmiServices/Common/Messages/Extraction/VerifiedFileStatus.cs
+++ b/src/SmiServices/Common/Messages/Extraction/VerifiedFileStatus.cs
@@ -1,30 +1,29 @@
-namespace SmiServices.Common.Messages.Extraction
+namespace SmiServices.Common.Messages.Extraction;
+
+public enum VerifiedFileStatus
{
- public enum VerifiedFileStatus
- {
- ///
- /// Unused placeholder value
- ///
- None = 0,
+ ///
+ /// Unused placeholder value
+ ///
+ None = 0,
- ///
- /// The file has not (yet) been verified
- ///
- NotVerified,
+ ///
+ /// The file has not (yet) been verified
+ ///
+ NotVerified,
- ///
- /// The file was scanned and determined to not be identifiable
- ///
- NotIdentifiable,
+ ///
+ /// The file was scanned and determined to not be identifiable
+ ///
+ NotIdentifiable,
- ///
- /// The file was scanned and determined to be identifiable
- ///
- IsIdentifiable,
+ ///
+ /// The file was scanned and determined to be identifiable
+ ///
+ IsIdentifiable,
- ///
- /// There was an error processing the file. Identifiability could not be determined
- ///
- ErrorWontRetry,
- }
+ ///
+ /// There was an error processing the file. Identifiability could not be determined
+ ///
+ ErrorWontRetry,
}
diff --git a/src/SmiServices/Common/Messages/FatalErrorMessage.cs b/src/SmiServices/Common/Messages/FatalErrorMessage.cs
index 75cd102a9..691f1737e 100644
--- a/src/SmiServices/Common/Messages/FatalErrorMessage.cs
+++ b/src/SmiServices/Common/Messages/FatalErrorMessage.cs
@@ -3,21 +3,20 @@
using Newtonsoft.Json;
using System;
-namespace SmiServices.Common.Messages
+namespace SmiServices.Common.Messages;
+
+public class FatalErrorMessage : MemberwiseEquatable, IMessage
{
- public class FatalErrorMessage : MemberwiseEquatable, IMessage
- {
- [JsonProperty(Required = Required.Always)]
- public string Message { get; set; } = null!;
+ [JsonProperty(Required = Required.Always)]
+ public string Message { get; set; } = null!;
- // TODO(rkm 2023-08-04) The nullability is confusing here. We should audit and remove all DisallowNull usages
- [JsonProperty(Required = Required.DisallowNull)]
- public Exception? Exception { get; set; }
+ // TODO(rkm 2023-08-04) The nullability is confusing here. We should audit and remove all DisallowNull usages
+ [JsonProperty(Required = Required.DisallowNull)]
+ public Exception? Exception { get; set; }
- public FatalErrorMessage(string message, Exception? exception)
- {
- Message = message;
- Exception = exception;
- }
+ public FatalErrorMessage(string message, Exception? exception)
+ {
+ Message = message;
+ Exception = exception;
}
}
diff --git a/src/SmiServices/Common/Messages/IFileReferenceMessage.cs b/src/SmiServices/Common/Messages/IFileReferenceMessage.cs
index b0e1a3765..ae603d1c7 100644
--- a/src/SmiServices/Common/Messages/IFileReferenceMessage.cs
+++ b/src/SmiServices/Common/Messages/IFileReferenceMessage.cs
@@ -1,13 +1,12 @@
-namespace SmiServices.Common.Messages
+namespace SmiServices.Common.Messages;
+
+///
+/// Describes an IMessage that references a dicom file in physical storage
+///
+public interface IFileReferenceMessage : IMessage
{
///
- /// Describes an IMessage that references a dicom file in physical storage
+ /// File path relative to the FileSystemRoot
///
- public interface IFileReferenceMessage : IMessage
- {
- ///
- /// File path relative to the FileSystemRoot
- ///
- string DicomFilePath { get; set; }
- }
+ string DicomFilePath { get; set; }
}
diff --git a/src/SmiServices/Common/Messages/IMessage.cs b/src/SmiServices/Common/Messages/IMessage.cs
index d3b89720e..c4ca01499 100644
--- a/src/SmiServices/Common/Messages/IMessage.cs
+++ b/src/SmiServices/Common/Messages/IMessage.cs
@@ -1,7 +1,6 @@
-namespace SmiServices.Common.Messages
-{
- ///
- /// Interface for any SMI message. Used to allow a constraint on a generic argument.
- ///
- public interface IMessage { }
-}
+namespace SmiServices.Common.Messages;
+
+///
+/// Interface for any SMI message. Used to allow a constraint on a generic argument.
+///
+public interface IMessage { }
diff --git a/src/SmiServices/Common/Messages/IMessageHeader.cs b/src/SmiServices/Common/Messages/IMessageHeader.cs
index 5c2b0df85..75a7a2947 100644
--- a/src/SmiServices/Common/Messages/IMessageHeader.cs
+++ b/src/SmiServices/Common/Messages/IMessageHeader.cs
@@ -2,26 +2,25 @@
using System;
using System.Collections.Generic;
-namespace SmiServices.Common.Messages
+namespace SmiServices.Common.Messages;
+
+public interface IMessageHeader
{
- public interface IMessageHeader
- {
- Guid MessageGuid { get; init; }
+ Guid MessageGuid { get; init; }
- int ProducerProcessID { get; init; }
+ int ProducerProcessID { get; init; }
- string ProducerExecutableName { get; init; }
+ string ProducerExecutableName { get; init; }
- long OriginalPublishTimestamp { get; init; }
+ long OriginalPublishTimestamp { get; init; }
- ///
- /// The full message chain from origin to here
- ///
- Guid[] Parents { get; }
+ ///
+ /// The full message chain from origin to here
+ ///
+ Guid[] Parents { get; }
- void Populate(IDictionary props);
- void Log(ILogger logger, LogLevel level, string message, Exception? ex = null);
+ void Populate(IDictionary props);
+ void Log(ILogger logger, LogLevel level, string message, Exception? ex = null);
- bool IsDescendantOf(IMessageHeader other);
- }
+ bool IsDescendantOf(IMessageHeader other);
}
diff --git a/src/SmiServices/Common/Messages/MessageHeader.cs b/src/SmiServices/Common/Messages/MessageHeader.cs
index b5c74b52c..3a7975490 100644
--- a/src/SmiServices/Common/Messages/MessageHeader.cs
+++ b/src/SmiServices/Common/Messages/MessageHeader.cs
@@ -7,140 +7,139 @@
using System.Linq;
using System.Text;
-namespace SmiServices.Common.Messages
+namespace SmiServices.Common.Messages;
+
+public class MessageHeader : MemberwiseEquatable, IMessageHeader
{
- public class MessageHeader : MemberwiseEquatable, IMessageHeader
- {
- public Guid MessageGuid { get; init; }
+ public Guid MessageGuid { get; init; }
- public int ProducerProcessID { get; init; }
+ public int ProducerProcessID { get; init; }
- public string ProducerExecutableName { get; init; }
+ public string ProducerExecutableName { get; init; }
- public long OriginalPublishTimestamp { get; init; }
+ public long OriginalPublishTimestamp { get; init; }
- public Guid[] Parents { get; init; }
- public const string Splitter = "->";
+ public Guid[] Parents { get; init; }
+ public const string Splitter = "->";
- private static readonly int _producerProcessID;
+ private static readonly int _producerProcessID;
- private static string? _currentProgramName;
- public static string CurrentProgramName
+ private static string? _currentProgramName;
+ public static string CurrentProgramName
+ {
+ get
{
- get
- {
- if (string.IsNullOrWhiteSpace(_currentProgramName))
- throw new Exception("Value must be set before use");
- return _currentProgramName;
- }
- set => _currentProgramName = value;
+ if (string.IsNullOrWhiteSpace(_currentProgramName))
+ throw new Exception("Value must be set before use");
+ return _currentProgramName;
}
+ set => _currentProgramName = value;
+ }
- static MessageHeader()
- {
- _producerProcessID = Environment.ProcessId;
- }
+ static MessageHeader()
+ {
+ _producerProcessID = Environment.ProcessId;
+ }
- [JsonConstructor]
- public MessageHeader()
- : this(parent: default) { }
+ [JsonConstructor]
+ public MessageHeader()
+ : this(parent: default) { }
- ///
- /// Declares that your process is about to send a message. Optionally as a result of processing another message ().
- ///
- /// The triggering message that caused you to want to send this message
- public MessageHeader(IMessageHeader? parent = null)
- {
- ProducerProcessID = _producerProcessID;
- ProducerExecutableName = CurrentProgramName;
- MessageGuid = Guid.NewGuid();
-
- if (parent == null)
- {
- Parents = [];
- OriginalPublishTimestamp = UnixTimeNow();
- }
- else
- {
- var p = new List(parent.Parents) { parent.MessageGuid };
- Parents = [.. p];
- OriginalPublishTimestamp = parent.OriginalPublishTimestamp;
- }
- }
+ ///
+ /// Declares that your process is about to send a message. Optionally as a result of processing another message ().
+ ///
+ /// The triggering message that caused you to want to send this message
+ public MessageHeader(IMessageHeader? parent = null)
+ {
+ ProducerProcessID = _producerProcessID;
+ ProducerExecutableName = CurrentProgramName;
+ MessageGuid = Guid.NewGuid();
- ///
- /// Creates a out of a (byte-encoded) header field set from RabbitMQ
- ///
- ///
- ///
- public static MessageHeader FromDict(IDictionary encodedHeaders, Encoding enc)
- => new()
- {
- MessageGuid = GetGuidArrayFromEncodedHeader(encodedHeaders["MessageGuid"], enc).Single(),
- ProducerProcessID = (int)encodedHeaders["ProducerProcessID"],
- ProducerExecutableName = enc.GetString((byte[])encodedHeaders["ProducerExecutableName"]),
- Parents = GetGuidArrayFromEncodedHeader(encodedHeaders["Parents"], enc),
- OriginalPublishTimestamp = Convert.ToInt64(encodedHeaders["OriginalPublishTimestamp"]),
- };
-
- ///
- /// Populates RabbitMQ header properties with the current MessageHeader
- ///
- ///
- public void Populate(IDictionary headers)
+ if (parent == null)
{
- headers.Add("MessageGuid", MessageGuid.ToString());
- headers.Add("ProducerProcessID", ProducerProcessID);
- headers.Add("ProducerExecutableName", ProducerExecutableName);
- headers.Add("OriginalPublishTimestamp", OriginalPublishTimestamp);
- headers.Add("Parents", string.Join(Splitter, Parents));
+ Parents = [];
+ OriginalPublishTimestamp = UnixTimeNow();
}
-
- public bool IsDescendantOf(IMessageHeader other)
+ else
{
- return Parents != null && Parents.Contains(other.MessageGuid);
+ var p = new List(parent.Parents) { parent.MessageGuid };
+ Parents = [.. p];
+ OriginalPublishTimestamp = parent.OriginalPublishTimestamp;
}
+ }
- public void Log(ILogger logger, LogLevel level, string message, Exception? ex = null)
+ ///
+ /// Creates a out of a (byte-encoded) header field set from RabbitMQ
+ ///
+ ///
+ ///
+ public static MessageHeader FromDict(IDictionary encodedHeaders, Encoding enc)
+ => new()
{
- //TODO This is massively over-logging - ProducerProcessID, ProducerExecutableName, OriginalPublishTimestamp are found in the logs anyway
- var theEvent = new LogEventInfo(level, logger.Name, message);
- theEvent.Properties["MessageGuid"] = MessageGuid.ToString();
- theEvent.Properties["ProducerProcessID"] = ProducerProcessID;
- theEvent.Properties["ProducerExecutableName"] = ProducerExecutableName;
- theEvent.Properties["OriginalPublishTimestamp"] = OriginalPublishTimestamp;
- theEvent.Properties["Parents"] = string.Join(Splitter, Parents);
- theEvent.Exception = ex;
-
- logger.Log(theEvent);
- }
+ MessageGuid = GetGuidArrayFromEncodedHeader(encodedHeaders["MessageGuid"], enc).Single(),
+ ProducerProcessID = (int)encodedHeaders["ProducerProcessID"],
+ ProducerExecutableName = enc.GetString((byte[])encodedHeaders["ProducerExecutableName"]),
+ Parents = GetGuidArrayFromEncodedHeader(encodedHeaders["Parents"], enc),
+ OriginalPublishTimestamp = Convert.ToInt64(encodedHeaders["OriginalPublishTimestamp"]),
+ };
+
+ ///
+ /// Populates RabbitMQ header properties with the current MessageHeader
+ ///
+ ///
+ public void Populate(IDictionary headers)
+ {
+ headers.Add("MessageGuid", MessageGuid.ToString());
+ headers.Add("ProducerProcessID", ProducerProcessID);
+ headers.Add("ProducerExecutableName", ProducerExecutableName);
+ headers.Add("OriginalPublishTimestamp", OriginalPublishTimestamp);
+ headers.Add("Parents", string.Join(Splitter, Parents));
+ }
- public override string ToString()
- {
- var sb = new StringBuilder();
- sb.Append("MessageGuid: " + MessageGuid);
- sb.Append(", ProducerProcessID: " + ProducerProcessID);
- sb.Append(", ProducerExecutableName: " + ProducerExecutableName);
- sb.Append(", OriginalPublishTimestamp:" + OriginalPublishTimestamp);
- sb.Append(", Parents: [" + string.Join(Splitter, Parents) + "]");
- return sb.ToString();
- }
+ public bool IsDescendantOf(IMessageHeader other)
+ {
+ return Parents != null && Parents.Contains(other.MessageGuid);
+ }
- // TODO(rkm 2020-03-08) Can't we just use the DateTime.UnixEpoch value here?
- public static long UnixTimeNow() => UnixTime(DateTime.UtcNow);
- public static long UnixTime(DateTime dateTime) => (long)(dateTime - new DateTime(1970, 1, 1, 0, 0, 0)).TotalSeconds;
- public static DateTime UnixTimeToDateTime(long unixTime) => new DateTime(1970, 1, 1, 0, 0, 0) + TimeSpan.FromSeconds(unixTime);
+ public void Log(ILogger logger, LogLevel level, string message, Exception? ex = null)
+ {
+ //TODO This is massively over-logging - ProducerProcessID, ProducerExecutableName, OriginalPublishTimestamp are found in the logs anyway
+ var theEvent = new LogEventInfo(level, logger.Name, message);
+ theEvent.Properties["MessageGuid"] = MessageGuid.ToString();
+ theEvent.Properties["ProducerProcessID"] = ProducerProcessID;
+ theEvent.Properties["ProducerExecutableName"] = ProducerExecutableName;
+ theEvent.Properties["OriginalPublishTimestamp"] = OriginalPublishTimestamp;
+ theEvent.Properties["Parents"] = string.Join(Splitter, Parents);
+ theEvent.Exception = ex;
+
+ logger.Log(theEvent);
+ }
- public static Guid[] GetGuidArray(string str)
- {
- string[] strings = str.Split(new[] { Splitter }, StringSplitOptions.RemoveEmptyEntries);
- return strings.Select(Guid.Parse).ToArray();
- }
+ public override string ToString()
+ {
+ var sb = new StringBuilder();
+ sb.Append("MessageGuid: " + MessageGuid);
+ sb.Append(", ProducerProcessID: " + ProducerProcessID);
+ sb.Append(", ProducerExecutableName: " + ProducerExecutableName);
+ sb.Append(", OriginalPublishTimestamp:" + OriginalPublishTimestamp);
+ sb.Append(", Parents: [" + string.Join(Splitter, Parents) + "]");
+ return sb.ToString();
+ }
- private static Guid[] GetGuidArrayFromEncodedHeader(object o, Encoding enc)
- {
- return GetGuidArray(enc.GetString((byte[])o));
- }
+ // TODO(rkm 2020-03-08) Can't we just use the DateTime.UnixEpoch value here?
+ public static long UnixTimeNow() => UnixTime(DateTime.UtcNow);
+ public static long UnixTime(DateTime dateTime) => (long)(dateTime - new DateTime(1970, 1, 1, 0, 0, 0)).TotalSeconds;
+ public static DateTime UnixTimeToDateTime(long unixTime) => new DateTime(1970, 1, 1, 0, 0, 0) + TimeSpan.FromSeconds(unixTime);
+
+ public static Guid[] GetGuidArray(string str)
+ {
+ string[] strings = str.Split(new[] { Splitter }, StringSplitOptions.RemoveEmptyEntries);
+ return strings.Select(Guid.Parse).ToArray();
+ }
+
+ private static Guid[] GetGuidArrayFromEncodedHeader(object o, Encoding enc)
+ {
+ return GetGuidArray(enc.GetString((byte[])o));
}
}
diff --git a/src/SmiServices/Common/Messages/RabbitMqXDeathHeaders.cs b/src/SmiServices/Common/Messages/RabbitMqXDeathHeaders.cs
index 6d892ba65..a471ae68f 100644
--- a/src/SmiServices/Common/Messages/RabbitMqXDeathHeaders.cs
+++ b/src/SmiServices/Common/Messages/RabbitMqXDeathHeaders.cs
@@ -7,173 +7,172 @@
using System.Linq;
using System.Text;
-namespace SmiServices.Common.Messages
+namespace SmiServices.Common.Messages;
+
+public class RabbitMqXDeathHeaders : MemberwiseEquatable
{
- public class RabbitMqXDeathHeaders : MemberwiseEquatable
- {
- public List XDeaths { get; set; }
+ public List XDeaths { get; set; }
- public string XFirstDeathExchange { get; set; }
+ public string XFirstDeathExchange { get; set; }
- public string XFirstDeathQueue { get; set; }
+ public string XFirstDeathQueue { get; set; }
- public string XFirstDeathReason { get; set; }
+ public string XFirstDeathReason { get; set; }
- public const string XDeathKey = "x-death";
- public const string XFirstDeathExchangeKey = "x-first-death-exchange";
- public const string XFirstDeathQueueKey = "x-first-death-queue";
- public const string XFristDeathReasonKey = "x-first-death-reason";
+ public const string XDeathKey = "x-death";
+ public const string XFirstDeathExchangeKey = "x-first-death-exchange";
+ public const string XFirstDeathQueueKey = "x-first-death-queue";
+ public const string XFristDeathReasonKey = "x-first-death-reason";
- private static readonly List _requiredKeys;
+ private static readonly List _requiredKeys;
- ///
- /// Static constructor
- ///
- static RabbitMqXDeathHeaders()
- {
- _requiredKeys =
- [
- XDeathKey,
- XFirstDeathExchangeKey,
- XFirstDeathQueueKey,
- XFristDeathReasonKey
- ];
- }
+ ///
+ /// Static constructor
+ ///
+ static RabbitMqXDeathHeaders()
+ {
+ _requiredKeys =
+ [
+ XDeathKey,
+ XFirstDeathExchangeKey,
+ XFirstDeathQueueKey,
+ XFristDeathReasonKey
+ ];
+ }
- public RabbitMqXDeathHeaders() { }
+ public RabbitMqXDeathHeaders() { }
- ///
- /// Creates a out of a (byte-encoded) header field set from RabbitMQ
- ///
- ///
- ///
- public RabbitMqXDeathHeaders(IDictionary encodedHeaders, Encoding enc)
- {
- if (!(encodedHeaders.Any() && _requiredKeys.All(encodedHeaders.ContainsKey)))
- throw new ArgumentException("xDeathEntry");
+ ///
+ /// Creates a out of a (byte-encoded) header field set from RabbitMQ
+ ///
+ ///
+ ///
+ public RabbitMqXDeathHeaders(IDictionary encodedHeaders, Encoding enc)
+ {
+ if (!(encodedHeaders.Any() && _requiredKeys.All(encodedHeaders.ContainsKey)))
+ throw new ArgumentException("xDeathEntry");
- XDeaths = [];
+ XDeaths = [];
- foreach (object xDeathEntry in (List