using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.ComponentModel; using System.Runtime.CompilerServices; using System.Linq; using System.Numerics; using System.Runtime; using System.Text; using System.Threading.Tasks; using System.Windows; using System.Windows.Media; using System.Windows.Input; using System.IO; using DuoVia.FuzzyStrings; using static WPFUI.MainWindow; using System.Text.RegularExpressions; namespace WPFUI { public class PTAccess { static void OrThrow(bool result, dynamic source) { if (!result) { string errorMessage = source.getErrorMessage(); MessageBox.Show($"{errorMessage}. Abort application! ", "Error ", MessageBoxButton.OK, MessageBoxImage.Error); App.Current.Shutdown(); // TERMINATE APPLICATION Application.Current.MainWindow.Close(); } } static bool ValidOrThrow(PatientDataItem result, dynamic source) { bool Success = true; if (!result.isValid()) { string errorMessage = source.getErrorMessage(); MessageBox.Show($"{errorMessage}. Enter patient name!", "Warning ", MessageBoxButton.OK, MessageBoxImage.Warning); Success = false; } return Success; } public bool FirstPass { get; set; } = true; public static VelocityEngine engine { get; set; } = new VelocityEngine(); public static Patient patient { get; set; } = null; public static string VelocityDataBase = ""; public static string UserName = ""; public static string Password = ""; public static string targetSetName = ""; public static Volume selectedCT { get; set; } = null; public static List cts { get; set; } = new List(); public static List ctsUID { get; set; } = new List(); public static string selectedCtUID { get; set; } = ""; public static List structSet { get; set; } = new List(); public static List StrId { get; set; } = new List(); private static StructureOperations sops1 = null; public static StructureOperations sops { get => sops1; set => sops1 = value; } public static List strucNames { get; set; } = new List(); public static List setsUID { get; set; } = new List(); public static List setsNames { get; set; } = new List(); public static List VelStructures { get; set; } = new List(); public static Volume primaryVolume { get; set; } = null; // public HashSet RenamedStructNames { get; set; } = new HashSet(); public List RenamedStructNames { get; set; } = new List(); /*-------------------------------------------------------------------------------------------------------------------------------------------------*/ /*----------------------------------------------------- INITIALIZE VELOCITY ------------------------------------------------------------*/ /*--------------------------------- Clean up Velocity data possibly left over by a previous run -----------------------------------*/ /*--------------------------------- Get a new Velocity Engine handle -------------------------------------------------------------------*/ public void InitializeVelocity() { AppDomain.CurrentDomain.ProcessExit += (source, data) => { PTAccess.engine.logout(); }; // CLEAN UP VELOCITY ENVIRONMENT engine = new VelocityEngine(); } /*-----------------------------------------------------------------------------------------------------------------------------------------------*/ /*------------------------------------------------ LOG INTO VELOCITY DATABASE ---------------------------------------------------*/ public void LogIntoVelocityDB() { // const string USER = "codey"; // const string PASS = "Rttqa123"; const string GRID_IP = "192.168.10.57"; const int GRID_PORT = 57000; // const string GRID_DB = "RTTQA-2"; Action orThrow = result => OrThrow(result, engine); orThrow(engine.loginToGrid(UserName, Password, GRID_IP, GRID_PORT, VelocityDataBase)); } /*----------------------------------------------------------------------------------------------------------------------------------------------------------------------*/ /*------------------------------------------- RESET VELOCITY ENVIRONMENT -------------------------------------------------------------------------*/ public void ResetVelocityContext() { if (!FirstPass) { engine.unloadPatient(); engine.unloadPrimaryVolume(); FirstPass = true; } } /*----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------*/ /*------------------------------------------------------------------------ GET CT VOLUMES ------------------------------------------------------------------------------*/ /*------------------------------------------ INPUT: Patient Identifier -----------------------------------------------------------------------------------------------------*/ /* */ public List GetCTVol(string PtId, ref int Res) { Mouse.OverrideCursor = System.Windows.Input.Cursors.Wait; // SET MOUSE TO HOURGLASS LogIntoVelocityDB(); // LOAD INTO VELOCITY DATABASE if (!engine.isLoggedIn()) // MAKE SURE VELOCITY ENGINE IS STILL CONNECTED { MessageBox.Show("Engine not connected ", "Error ", MessageBoxButton.OK, MessageBoxImage.Error); AppDomain.CurrentDomain.ProcessExit += (source, data) => { PTAccess.engine.logout(); }; // CLEAN UP VELOCITY ENVIRONMENT return null; } patient = engine.loadPatientByPatientId(PtId); // LOAD PATIENT if(!ValidOrThrow(patient, engine)) // CHECK PATIENT HAS BEEN LOADED { Res = -1; Mouse.OverrideCursor = null; // SHOW DEFAULT MOUSE return null; } var toList = patient.getVolumes("CT"); // GET PATIENT CT VOLUMES if (toList == null) { MessageBox.Show("No CT volume found for current patient!", "Warning", MessageBoxButton.OK, MessageBoxImage.Warning); // App.Current.Shutdown(); } List CTname = new List(); cts.Clear(); CTname.Clear(); ctsUID.Clear(); cts.AddRange(toList.Select (ct => ct)); // STORE CT VOLUMEs ctsUID.AddRange(toList.Select(ct => ct.getVolumeUID())); // STORE CT UIDs CTname.AddRange(toList.Select(ct => ct.getName())); // STORE CT NAMEs Mouse.OverrideCursor = null; // SHOW DEFAULT MOUSE return CTname; } /*------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------*/ /*-------------------- Handler triggered by selecting a CT from the list -----------------------------------------------------------------------------------------------------*/ /*----------------------------------------------------------------------- GET STRUCTURE SETS --------------------------------------------------------------------------------*/ /*-------------------- INPUT: Index of selected CT name -----------------------------------------------------------------------------------------------------------------------*/ public List GetStructSet(int ctInd, int Res) { Mouse.OverrideCursor = System.Windows.Input.Cursors.Wait; // SET MOUSE TO HOURGLASS if (!engine.isLoggedIn()) // MAKE SURE VELOCITY ENGINE IS STILL CONNECTED { MessageBox.Show("Engine not connected. Abort script!", "Error ", MessageBoxButton.OK, MessageBoxImage.Error); App.Current.Shutdown(); // TERMINATE APPLICATION IF ENGINE IS LOGGED OUT } if (!FirstPass) // IF THIS IS NOT THE FIRST CT SELECTION THEN UNLOAD THE PRIMARY VOLUME { engine.unloadPrimaryVolume(); // MAKE SURE CURRENT PRIMARY CT VOLUME HAS BEEN UNLOADED } selectedCtUID = ctsUID[ctInd]; if (String.IsNullOrWhiteSpace(selectedCtUID)) { MessageBox.Show("Selected CT UID not valid. Abort script!.", "Error ", MessageBoxButton.OK, MessageBoxImage.Error); return null; } try { if(!ValidOrThrow(engine.loadPrimaryVolumeByUID(selectedCtUID), engine)) { MessageBox.Show($"Primary Volume not loaded!", "Error ", MessageBoxButton.OK, MessageBoxImage.Error); Res = -2; return null; } else { primaryVolume = engine.getPrimaryVolume(); } } catch (Exception ex) { MessageBox.Show($"{ex.Message}. Abort script!.", "Error ", MessageBoxButton.OK, MessageBoxImage.Error); return null; } FirstPass = false; /* if(!primaryVolume.isValid()) { MessageBox.Show("Primary Volume not valid. Abort script!.", "Error ", MessageBoxButton.OK, MessageBoxImage.Warning); App.Current.Shutdown(); // TERMINATE APPLICATION IF VELOCITY METHOD FAILS } */ // Get handle to StructureOperation class. Necessary to access structure properties sops = engine.getStructureOperations(); // Get structure sets linked to the selected CT volume List stSet = new List(); try { stSet = primaryVolume.getStructureSets().ToList(); } catch (Exception ex) { MessageBox.Show($"{ex.Message}. Abort script!.", "Error ", MessageBoxButton.OK, MessageBoxImage.Error); return null; } if (stSet.Count <= 0) { MessageBox.Show($"No structure set found for this CT.", "Warning ", MessageBoxButton.OK, MessageBoxImage.Warning); Mouse.OverrideCursor = null; // SHOW DEFAULT MOUSE return null; } structSet.Clear(); setsNames.Clear(); setsUID.Clear(); structSet.AddRange(stSet.Select (st => st)); // Store Structure-Set VOLUMEs setsUID.AddRange(stSet.Select(st => st.getVolumeUID())); // Store Structure-Set UIDs // string DbgList = ""; // DbgList = String.Join(", ", setsUID.Select(x => x)); // DEBUG PURPOSE // MessageBox.Show($"STRUCTURE-SETs UID: \n {DbgList}"); // DEBUG PURPOSE setsNames.AddRange(stSet.Select(st => st.getName())); // STORE Structure-Set NAMEs // string DbgList = ""; // DbgList = String.Join(", ", setsNames.Select(x => x)); // DEBUG PURPOSE // MessageBox.Show($"STRUCTURE-SET NAMEs: \n {DbgList}"); // DEBUG PURPOSE Mouse.OverrideCursor = null; // SHOW DEFAULT MOUSE return setsNames; } /*----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------*/ /* Method called by selecting a Structure-Set from the list */ /*----------------------------------------------------------------------- GET PATIENT STRUCTURES -------------------------------------------------------------------------------*/ public List GetStructures(int SetInd, string PtId) { int Res = 0; if (!engine.isLoggedIn()); // MAKE SURE VELOCITY ENGINE IS STILL CONNECTED { MessageBox.Show("Engine logged out.", "Retry connection", MessageBoxButton.OK, MessageBoxImage.Warning); Mouse.OverrideCursor = System.Windows.Input.Cursors.Wait; // SET CURSOR TO HOURGLASS LogIntoVelocityDB(); patient = engine.loadPatientByPatientId(PtId); // LOAD PATIENT if(!ValidOrThrow(patient, engine)) { Res = -1; return null; } if(!ValidOrThrow(engine.loadPrimaryVolumeByUID(selectedCtUID), engine)) { Res = -2; return null; } primaryVolume = engine.getPrimaryVolume(); sops = engine.getStructureOperations(); } string ssName = setsNames[SetInd]; StructureSet primarySet = primaryVolume.getStructureSets().Where(ss => ss.getName() == ssName).First(); if(!primarySet.isValid()) // MEMORY-ACCESS-VIOLATION EXCEPTION HERE!!! { MessageBox.Show("Selected structure-set not valid!", "Error ", MessageBoxButton.OK, MessageBoxImage.Error); return null; } VelStructures.Clear(); VelStructures = primarySet.getStructures().ToList(); StrId.Clear(); strucNames.Clear(); StrId.AddRange(VelStructures.Select (st => st.getVelocityId())); // Store Structure VOLUME IDs // string DbgList = ""; // DbgList = String.Join(", ", StrId.Select(x => x)); // DEBUG PURPOSE // MessageBox.Show($"STRUTURE IDs FOR SELECTED STRUCTURE-SET: \n {DbgList}"); // DEBUG PURPOSE strucNames.AddRange(VelStructures.Select(st => st.getName())); // Store Structure NAMEs // DbgList = ""; // DbgList = String.Join(", ", strucNames.Select(x => x)); // DEBUG PURPOSE // MessageBox.Show($"STRUTURE NAMEs FOR SELECTED STRUCTURE-SET: \n {DbgList}"); // DEBUG PURPOSE Mouse.OverrideCursor = null; // SHOW DEFAULT MOUSE return strucNames; } private static string WildCardToRegular(string value) { return "^" + Regex.Escape(value).Replace("\\*", ".*") + "$"; } /*--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------*/ /*-------------------------------------------------- AUTOMATICALLY RENAME STRUCTURES -------------------------------------------------------------------------*/ /*---------------- INPUT: List of protocol structure from external database ------------------------------------------------------------------------------------------*/ /* Duplicate structure set and rename structures ............................................................................................................................................*/ /* Try to find a matching protocol structure for not-found Velocity structures through String-Matching-Algorithms ............................................*/ public List StructuresAutomaticRename(List protNames, ref List guessed, double DTh) { MessageBox.Show($"DiceThreshold: {DTh}", "Information", MessageBoxButton.OK, MessageBoxImage.Information); Mouse.OverrideCursor = System.Windows.Input.Cursors.Wait; // SET MOUSE TO HOURGLASS int strucNamesLength = strucNames.Count; // DEBUG CHECK-POINT RenamedStructNames.Clear(); for (int k = 0; k < strucNames.Count; k++) { if (protNames.Contains(strucNames[k])) { RenamedStructNames.Add(strucNames[k]); } else RenamedStructNames.Add(" "); } double diceCoef = 0; for (int k = 0; (k < strucNamesLength); k++) { if (!protNames.Contains(strucNames[k])) { if(Regex.IsMatch(strucNames[k], @"(G|P|I|C)TV\d+_\d+") | Regex.IsMatch(strucNames[k], @"(I)CTV\d+_\d+") | Regex.IsMatch(strucNames[k], @"(I)GTV\d+_\d+")) { int index = strucNames[k].IndexOf("_"); string substr = strucNames[k].Substring(index + 1); if(substr.Length > 0) { if (substr.All(char.IsDigit)) { RenamedStructNames[k] = strucNames[k]; } } } else if (strucNames[k].IndexOf('_') >= 0 & strucNames[k].IndexOf('_') > strucNames[k].Length -1) { string substr = strucNames[k].Substring(strucNames[k].IndexOf('_') + 1); if (substr.All(char.IsDigit)) { RenamedStructNames[k] = strucNames[k]; } } else { string DiceGuess = ""; diceCoef = DiceCoeff(strucNames[k], protNames, ref DiceGuess); if (RenamedStructNames.Contains(DiceGuess) | (diceCoef < DTh)) // IF STRING ADDED THEN ADD A WHITE STRING { RenamedStructNames[k] = " "; } else { RenamedStructNames[k] = DiceGuess; guessed[k] = true; } } } } // string DbgList = ""; // DbgList = String.Join(", ", RenamedStructNames.Select(x => x)); // DEBUG PURPOSE // MessageBox.Show($"AUTOMATICALLY RENAMED STRUCTUREs: \n {DbgList}"); // DEBUG PURPOSE if(RenamedStructNames.Count != strucNames.Count) { MessageBox.Show("Velocity Structures number not same as Renamed Structures number"); } int RenamedStrucNamesLength = RenamedStructNames.Count; // DEBUG CHECK-POINT Mouse.OverrideCursor = null; // SHOW DEFAULT MOUSE return (List) RenamedStructNames; } /*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ /*+++++++++++++++++++++++++++++++++++++++++++++++++ STRING MATCHING ALGORITHMS ++++++++++++++++++++++++++++++++++++++++++++++++++++*/ /* --- DICE COEFFICIENT --- */ private static double DiceCoeff(string name, List ProtRTStruct, ref string DiceGuess) { double dc = 0; DiceGuess = ""; foreach (var st in ProtRTStruct) { double dice = name.DiceCoefficient(st); // Console.WriteLine($"\t {name} against {st} Dice: {dice}"); if (dc < dice) { dc = dice; DiceGuess = st; } } return dc; } /*-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------*/ /*------------------------------------------------------ WRITE RENAMED SELECTED STRUCTUREs TO VELOCITY DATABASE ---------------------------------------------------------------------------------*/ /* */ public bool StoreSavedStructuresToVelocityDB(ObservableCollection AutoNames) { // Create a new structure set .................................................................................................................................................... string fixedName = "Proto"; DateTime aDate = DateTime.Now; string day = aDate.Day.ToString(); string month = aDate.Month.ToString(); string year = aDate.Year.ToString(); string localDate = day + "-" + month + "-" + year; targetSetName = fixedName + localDate; StructureSet targetSet = sops.createStructureSet(targetSetName, true); ValidOrThrow(targetSet, sops); /*............................................. Generate a Mask for each checked structure ..................................................................................................................................................*/ int AutoNamesCount = AutoNames.Count; // DEBUG CHECK-POINT // string DbgList = ""; // DEBUG PURPOSE bool res = true; int NumStructsToSave = 0; string totNames = ""; // DEBUG PURPOSE for (int k = 0; k < AutoNames.Count; k++) { if (AutoNames[k].IsAccepted && !string.IsNullOrWhiteSpace(AutoNames[k].StrName)) { AutoNames[k].StrName = AutoNames[k].StrName.Trim(charsToTrim); // TRIM HEADING AND TRAILING SPACES totNames += AutoNames[k].StrName; // DEBUG PURPOSE StructureMask strucMsk = sops.getVolumetricStructure(StrId[k]); // Create a mask for current structure Structure newStruct = sops.createStructureFromMask​(targetSet.getVelocityId(), strucMsk, AutoNames[k].StrName, "Organ"); // Create structure if (newStruct == null) { MessageBox.Show($"Edited structure not correctly created in Velocity DB", "Error ", MessageBoxButton.OK, MessageBoxImage.Error); res = false; } else { NumStructsToSave = +1; } } } // DbgList = String.Join(", ", totNames); // MessageBox.Show($"NAMEs OF USER SELECTED RENAMED STRUCTUREs: \n {DbgList}"); // DEBUG PURPOSE /*........................................................ IMPORTANT: call save after finishing a set of modifications to a structure set .......................................................................................*/ /*............................................. If no structure selected nothing is written to Velocity database ..........................................................................................................................*/ if (NumStructsToSave == 0) { MessageBox.Show("No structure selected to save to Velocity database", "Warning ", MessageBoxButton.OK, MessageBoxImage.Warning); res = false; } else { targetSet = sops.saveStructureSet(targetSet.getVelocityId()); if (targetSet == null) { res = false; } } return res; } } }