266 lines
11 KiB
C#
266 lines
11 KiB
C#
using System;
|
|
using MySql.Data.MySqlClient;
|
|
using FellowOakDicom;
|
|
using FellowOakDicom.Network;
|
|
using FellowOakDicom.Network.Client;
|
|
using Microsoft.Extensions.Configuration;
|
|
using System.Collections.Generic;
|
|
using System.Threading.Tasks;
|
|
using Microsoft.Extensions.Logging;
|
|
using Microsoft.Extensions.DependencyInjection;
|
|
using System.Threading;
|
|
|
|
namespace DicomMigratorApp
|
|
{
|
|
class Program
|
|
{
|
|
private static IConfiguration Configuration;
|
|
private static ILogger<Program> _logger;
|
|
|
|
private static string connectionString;
|
|
private static string targetIP;
|
|
private static string senderAET;
|
|
private static string targetAET;
|
|
private static int targetPort;
|
|
|
|
static async Task Main(string[] args)
|
|
{
|
|
var serviceProvider = new ServiceCollection()
|
|
.AddLogging(config =>
|
|
{
|
|
config.AddConsole();
|
|
config.SetMinimumLevel(LogLevel.Information);
|
|
})
|
|
.BuildServiceProvider();
|
|
|
|
_logger = serviceProvider.GetService<ILoggerFactory>()
|
|
.CreateLogger<Program>();
|
|
|
|
_logger.LogInformation("Application started");
|
|
|
|
connectionString = Environment.GetEnvironmentVariable("ConnectionStrings__DefaultConnection") ?? "";
|
|
targetIP = Environment.GetEnvironmentVariable("Dest__IP") ?? "";
|
|
senderAET = Environment.GetEnvironmentVariable("Sender__AET") ?? "";
|
|
targetAET = Environment.GetEnvironmentVariable("Dest__AET") ?? "";
|
|
var targetPortStr = Environment.GetEnvironmentVariable("Dest__Port") ?? "";
|
|
|
|
if (!int.TryParse(targetPortStr, out targetPort))
|
|
{
|
|
_logger.LogError("TargetServer__Port environment variable is not a valid integer.");
|
|
return;
|
|
}
|
|
|
|
while (true)
|
|
{
|
|
try
|
|
{
|
|
var migrationStudy = await GetMigrationStudy(connectionString);
|
|
if (migrationStudy == null)
|
|
{
|
|
await Task.Delay(5000);
|
|
continue;
|
|
}
|
|
|
|
_logger.LogInformation($"Retrieved study details for migration: {migrationStudy.StudyInstanceUID}");
|
|
|
|
try
|
|
{
|
|
var imagePaths = await GetImagePaths(connectionString, migrationStudy.StudyInstanceUID);
|
|
|
|
foreach (var image in imagePaths)
|
|
{
|
|
await MigrateImage(image.Path);
|
|
await UpdateImagePathStatus(connectionString, image.Id, "MIGRATION_COMPLETE");
|
|
}
|
|
|
|
await UpdateSourceCFind(connectionString, migrationStudy.Id, "MIGRATION_COMPLETE");
|
|
}
|
|
catch (DicomAssociationRejectedException ex)
|
|
{
|
|
_logger.LogError(ex, $"Association rejected for studyInstanceUID {migrationStudy.StudyInstanceUID}");
|
|
await UpdateSourceCFind(connectionString, migrationStudy.Id, "MIGRATION_ERRORED");
|
|
break;
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
_logger.LogError(ex, $"An error occurred while retrieving or saving studyInstanceUID {migrationStudy.StudyInstanceUID}");
|
|
await UpdateSourceCFind(connectionString, migrationStudy.Id, "MIGRATION_ERRORED");
|
|
break;
|
|
}
|
|
}
|
|
catch (MySqlException ex)
|
|
{
|
|
_logger.LogError(ex, "An error occurred while connecting to the database");
|
|
break;
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
_logger.LogError(ex, "An error occurred in the main processing loop");
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
private static async Task<SourceStudy> GetMigrationStudy(string connectionString)
|
|
{
|
|
var qrStudy = new SourceStudy();
|
|
|
|
try
|
|
{
|
|
using (var connection = new MySqlConnection(connectionString))
|
|
{
|
|
_logger.LogInformation("Opening a connection to the database");
|
|
await connection.OpenAsync();
|
|
_logger.LogInformation("Database connection established");
|
|
var command = new MySqlCommand("SELECT id, study_instance_uid, patient_id, status FROM priority_migration_study WHERE status='QR_COMPLETE' ORDER BY schedule_date LIMIT 1", connection);
|
|
var reader = await command.ExecuteReaderAsync();
|
|
|
|
while (await reader.ReadAsync())
|
|
{
|
|
qrStudy = new SourceStudy
|
|
{
|
|
Id = reader.IsDBNull(0) ? 0 : reader.GetInt32(0),
|
|
StudyInstanceUID = reader.IsDBNull(1) ? null : reader.GetString(1),
|
|
PatientID = reader.IsDBNull(2) ? null : reader.GetString(2),
|
|
Status = reader.IsDBNull(3) ? null : reader.GetString(3)
|
|
};
|
|
}
|
|
|
|
reader.Close();
|
|
}
|
|
if (qrStudy.StudyInstanceUID != null)
|
|
{
|
|
_logger.LogInformation($"Picked study to be migrated from the database: {qrStudy.StudyInstanceUID}");
|
|
await UpdateSourceCFind(connectionString, qrStudy.Id, "MIGRATION_INPROGRESS");
|
|
}
|
|
else
|
|
{
|
|
qrStudy = null;
|
|
}
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
_logger.LogError(ex, "Error retrieving study to be migrated from the database");
|
|
throw; // Re-throw the exception to ensure it is caught by the outer try-catch block
|
|
}
|
|
|
|
return qrStudy;
|
|
}
|
|
|
|
private static async Task<List<ImageRecord>> GetImagePaths(string connectionString, string studyInstanceUID)
|
|
{
|
|
var imagePaths = new List<ImageRecord>();
|
|
|
|
try
|
|
{
|
|
using (var connection = new MySqlConnection(connectionString))
|
|
{
|
|
await connection.OpenAsync();
|
|
var command = new MySqlCommand("SELECT id, image_path FROM priority_migration_image WHERE study_instance_uid = @StudyInstanceUID AND (status != 'MIGRATION_COMPLETE' or status is null)", connection);
|
|
command.Parameters.AddWithValue("@StudyInstanceUID", studyInstanceUID);
|
|
var reader = await command.ExecuteReaderAsync();
|
|
|
|
while (await reader.ReadAsync())
|
|
{
|
|
imagePaths.Add(new ImageRecord
|
|
{
|
|
Id = reader.IsDBNull(0) ? 0 : reader.GetInt32(0),
|
|
Path = reader.IsDBNull(1) ? null : reader.GetString(1)
|
|
});
|
|
}
|
|
|
|
reader.Close();
|
|
}
|
|
_logger.LogInformation($"Retrieved {imagePaths.Count} image paths for studyInstanceUID {studyInstanceUID}");
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
_logger.LogError(ex, "Error retrieving image paths from the database");
|
|
throw; // Re-throw the exception to ensure it is caught by the outer try-catch block
|
|
}
|
|
|
|
return imagePaths;
|
|
}
|
|
|
|
private static async Task UpdateImagePathStatus(string connectionString, int imageId, string updateStatus)
|
|
{
|
|
try
|
|
{
|
|
using (var connection = new MySqlConnection(connectionString))
|
|
{
|
|
await connection.OpenAsync();
|
|
var command = new MySqlCommand("UPDATE priority_migration_image SET status = @Status WHERE id = @ImageId", connection);
|
|
|
|
command.Parameters.AddWithValue("@ImageId", imageId);
|
|
command.Parameters.AddWithValue("@Status", updateStatus);
|
|
|
|
await command.ExecuteNonQueryAsync();
|
|
_logger.LogInformation($"Updated image path record for image ID {imageId} with status {updateStatus}");
|
|
}
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
_logger.LogError(ex, $"Error updating image path record for image ID {imageId}");
|
|
throw; // Re-throw the exception to ensure it is caught by the outer try-catch block
|
|
}
|
|
}
|
|
|
|
private static async Task MigrateImage(string imagePath)
|
|
{
|
|
try
|
|
{
|
|
var client = DicomClientFactory.Create(targetIP, targetPort, false, senderAET, targetAET);
|
|
|
|
var dicomFile = await DicomFile.OpenAsync(imagePath);
|
|
await client.AddRequestAsync(new DicomCStoreRequest(dicomFile));
|
|
|
|
await client.SendAsync();
|
|
|
|
_logger.LogInformation($"Migrated image {imagePath} to target AET {targetAET}");
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
_logger.LogError(ex, $"Error migrating image {imagePath}");
|
|
throw; // Re-throw the exception to ensure it is caught by the outer try-catch block
|
|
}
|
|
}
|
|
|
|
private static async Task UpdateSourceCFind(string connectionString, int studyId, string updateStatus)
|
|
{
|
|
try
|
|
{
|
|
using (var connection = new MySqlConnection(connectionString))
|
|
{
|
|
await connection.OpenAsync();
|
|
var command = new MySqlCommand("UPDATE priority_migration_study SET status = @Status WHERE id = @StudyId", connection);
|
|
|
|
command.Parameters.AddWithValue("@StudyId", studyId);
|
|
command.Parameters.AddWithValue("@Status", updateStatus);
|
|
|
|
await command.ExecuteNonQueryAsync();
|
|
_logger.LogInformation($"Updated source C-FIND record for study ID {studyId} with status {updateStatus}");
|
|
}
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
_logger.LogError(ex, $"Error updating source study record for study ID {studyId}");
|
|
throw; // Re-throw the exception to ensure it is caught by the outer try-catch block
|
|
}
|
|
}
|
|
}
|
|
|
|
class SourceStudy
|
|
{
|
|
public int Id { get; set; }
|
|
public string PatientID { get; set; }
|
|
public string StudyInstanceUID { get; set; }
|
|
public string Status { get; set; }
|
|
}
|
|
|
|
class ImageRecord
|
|
{
|
|
public int Id { get; set; }
|
|
public string Path { get; set; }
|
|
}
|
|
}
|