public async Task <RoutedItem> FindSCU(int taskID, RoutedItem routedItem, DcmtkConnection connection) { Connection = connection; //EX: findscu -P -k "(0008,0052)=SERIES" -k "(0010,0010)" -k "(0010,0020)" -k "(0010,0020)" -k "(0008,0050)" -k "(0020,000D)" -k "(0008,0005)" -k "(0020,1208)" -aec DCMTK localhost 11120 var taskInfo = $"task: {taskID} connection: {Connection.name}"; var stopWatch = new Stopwatch(); stopWatch.Start(); Dictionary <string, string> args = new Dictionary <string, string>(); var proc = new Process(); var procinfo = new ProcessStartInfo(); try { LiCloudRequest cFindParams = LiCloudRequest.FromJson(routedItem.request, _logger); foreach (var tag in cFindParams.searchTags) { _logger.Log(LogLevel.Debug, $"{taskInfo} id: {routedItem.id} tag: {tag.Key} {tag.Value}"); } cFindParams.searchTags.TryGetValue("0008,0052", out string cfindlevel); switch (cfindlevel) { case "PATIENT": args.TryAdd("level", "-P"); break; case "WORKLIST": args.TryAdd("level", "-W"); break; case "STUDY": default: args.TryAdd("level", "-S"); args.TryAdd("0008,0052", "-k \"(0008,0052)=STUDY\""); break; } //MessageID: {cFind.MessageID} _logger.Log(LogLevel.Information, $"{taskInfo} Request id: {routedItem.id} attempt: {routedItem.attempts}"); //default return tags args.TryAdd("0008,0005", "-k \"(0008,0005)\""); //Specific Character Set args.TryAdd("0008,0050", "-k \"(0008,0050)\""); //AccessionNumber args.TryAdd("0008,0054", "-k \"(0008,0054)\""); //Retrieve AE Title args.TryAdd("0020,1208", "-k \"(0020,1208)\""); //NumberOfStudyRelatedInstances args.TryAdd("0020,1209", "-k \"(0020,1209)\""); //NumberOfSeriesRelatedInstances args.TryAdd("0008,0060", "-k \"(0008,0060)\""); //Modality args.TryAdd("0008,0061", "-k \"(0008,0061)\""); //ModalitiesInStudy args.TryAdd("0010,0020", "-k \"(0010,0020)\""); //PatientID args.TryAdd("0010,0010", "-k \"(0010,0010)\""); //PatientName args.TryAdd("0010,0030", "-k \"(0010,0030)\""); //PatientBirthDate args.TryAdd("0010,0040", "-k \"(0010,0040)\""); //PatientSex args.TryAdd("0008,0020", "-k \"(0008,0020)\""); //StudyDate args.TryAdd("0020,0010", "-k \"(0020,0010)\""); //StudyID args.TryAdd("0020,000d", "-k \"(0020,000D)\""); //StudyInstanceUID args.TryAdd("0008,1030", "-k \"(0008,1030)\""); //StudyDescription // add the search tags foreach (KeyValuePair <string, string> tag in cFindParams.searchTags) { try { var exists = args.TryGetValue(tag.Key, out string arg); var newArg = $"-k \"({tag.Key})={tag.Value}\""; if (exists) { args[tag.Key] = newArg; } else { args[tag.Key] = newArg; } } catch (Exception e) { _logger.LogFullException(e, taskInfo); } } args.TryAdd("host", $"-aec {Connection.remoteAETitle} -aet {Connection.localAETitle} {Connection.remoteHostname} {Connection.remotePort}"); var profile = _profileStorage.Current; //connect the cFind to the RoutedItem that originated the request //the cache was already primed in GetRequests routedItem.fromConnection = Connection.name; routedItem.toConnections.Clear(); routedItem.status = RoutedItem.Status.PENDING; procinfo.UseShellExecute = false; procinfo.RedirectStandardError = true; procinfo.RedirectStandardOutput = true; procinfo.CreateNoWindow = true; if (profile.dcmtkLibPath != null) { procinfo.FileName = profile.dcmtkLibPath + Path.DirectorySeparatorChar + "bin" + Path.DirectorySeparatorChar + "findscu"; var DCMDICTPATH = profile.dcmtkLibPath + Path.DirectorySeparatorChar + Constants.Dirs.share + Path.DirectorySeparatorChar + Constants.Dirs.dcmtk + Path.DirectorySeparatorChar + "dicom.dic"; DCMDICTPATH += _util.EnvSeparatorChar() + profile.dcmtkLibPath + Path.DirectorySeparatorChar + Constants.Dirs.share + Path.DirectorySeparatorChar + Constants.Dirs.dcmtk + Path.DirectorySeparatorChar + "acrnema.dic"; DCMDICTPATH += _util.EnvSeparatorChar() + profile.dcmtkLibPath + Path.DirectorySeparatorChar + Constants.Dirs.share + Path.DirectorySeparatorChar + Constants.Dirs.dcmtk + Path.DirectorySeparatorChar + "diconde.dic"; DCMDICTPATH += _util.EnvSeparatorChar() + profile.dcmtkLibPath + Path.DirectorySeparatorChar + Constants.Dirs.share + Path.DirectorySeparatorChar + Constants.Dirs.dcmtk + Path.DirectorySeparatorChar + "private.dic"; procinfo.Environment.Add("DCMDICTPATH", DCMDICTPATH); } else { procinfo.FileName = "findscu"; } procinfo.Arguments = string.Join(" ", args.Values.ToArray());; proc.StartInfo = procinfo; List <string> response = new List <string>(); proc.OutputDataReceived += (object sendingProcess, DataReceivedEventArgs outLine) => { try { if (!string.IsNullOrEmpty(outLine.Data)) { response.Add(outLine.Data); } } catch (Exception e) { _logger.Log(LogLevel.Information, $"{e.Message}{e.StackTrace}"); } }; proc.ErrorDataReceived += (object sendingProcess, DataReceivedEventArgs outLine) => { try { if (!string.IsNullOrEmpty(outLine.Data)) { response.Add(outLine.Data); } } catch (Exception e) { _logger.Log(LogLevel.Information, $"{e.Message}{e.StackTrace}"); } }; proc.EnableRaisingEvents = true; proc.Exited += OnProcExit; _logger.Log(LogLevel.Information, $"{taskInfo} starting {procinfo.FileName} {procinfo.Arguments}"); proc.Start(); proc.BeginOutputReadLine(); proc.BeginErrorReadLine(); //proc.WaitForExit(); while (!proc.HasExited) { _logger.Log(LogLevel.Information, $"{taskInfo} {procinfo.FileName} is running..."); await Task.Delay(1000, _taskManager.cts.Token).ConfigureAwait(false); } // if (proc.ExitCode != 0) // { // _logger.Log(LogLevel.Warning, $"{taskInfo} {procinfo.FileName} ExitCode: {proc.ExitCode}"); // return false; // } _logger.Log(LogLevel.Information, $"{taskInfo} Response id: {routedItem.id} status: {proc.ExitCode.ToString()} elapsed: {stopWatch.Elapsed}"); Dictionary <string, string> returnTagData = new Dictionary <string, string>(); string jsonResults = null; string key = "response"; Dictionary <string, Dictionary <string, string> > results = new Dictionary <string, Dictionary <string, string> >(); if (response != null) { // scrape the stdout/stderr responses for the answers int i = 0; foreach (var responseItem in response) { _logger.Log(LogLevel.Debug, $"{taskInfo} Response id: {routedItem.id}, {responseItem}"); if (responseItem != null) { returnTagData.Add($"raw{++i}", responseItem); if (responseItem.Length >= 12) { if (responseItem.StartsWith("I: ---------------------------")) { } var test1 = responseItem.Substring(4, 9); var result = args.Keys.Any(e => e.Contains(test1)); if (args.Keys.Any(e => e.Contains(responseItem.Substring(4, 9)))) { string data = null; if (responseItem.Contains("no value available")) { data = ""; } else { data = responseItem.Substring(responseItem.IndexOf("[") + 1, responseItem.LastIndexOf("]") - responseItem.IndexOf("[") - 1).Trim().Trim('\0'); } if (responseItem.Substring(3, 11) == "(0008,0054)") //BOUR-940 mask AETitle with conn name { returnTagData.Add(responseItem.Substring(3, 11), Connection.name); } else { returnTagData.Add(responseItem.Substring(3, 11), data); } } if (responseItem.Contains("0020,000d"))//StudyInstanceUID { key = responseItem.Substring(responseItem.IndexOf("[") + 1, responseItem.LastIndexOf("]") - responseItem.IndexOf("[") - 1).Trim().Trim('\0'); } } else { if (responseItem.Equals("I: ") && key != "response") { if (proc.ExitCode != 0) { returnTagData.Add("StatusCode", proc.ExitCode.ToString()); } results.Add(key, new Dictionary <string, string>(returnTagData)); jsonResults = JsonSerializer.Serialize(results); routedItem.response.Add(jsonResults); results.Clear(); returnTagData.Clear(); key = "response"; } } } } } if (routedItem.response.Count == 0) { //per Victoria she doesn't want status code if there is no result if (proc.ExitCode != 0) { returnTagData.Add("StatusCode", proc.ExitCode.ToString()); } results.Add(key, returnTagData); jsonResults = JsonSerializer.Serialize(results); // set results in RoutedItem routedItem.response.Add(jsonResults); } RoutedItem toCache = null; switch (proc.ExitCode) { case 0: routedItem.status = RoutedItem.Status.COMPLETED; routedItem.resultsTime = DateTime.Now; _routedItemManager.Init(routedItem); _routedItemManager.Dequeue(Connection, Connection.toFindSCU, nameof(Connection.toFindSCU), error: false); toCache = (RoutedItem)_routedItemManager.Clone(); toCache.fromConnection = Connection.name; toCache.toConnections.Clear(); //BOUR-863 the toConnections on the toCache object weren't being cleared before rules so it contained DICOMConnection which toCache.attempts = 0; toCache.lastAttempt = DateTime.MinValue; toCache.type = RoutedItem.Type.RPC; _routedItemManager.Init(toCache); _routedItemManager.Enqueue(Connection, Connection.toRules, nameof(Connection.toRules)); break; default: routedItem.status = RoutedItem.Status.FAILED; routedItem.resultsTime = DateTime.Now; _routedItemManager.Init(routedItem); _routedItemManager.Dequeue(Connection, Connection.toFindSCU, nameof(Connection.toFindSCU), error: true); toCache = (RoutedItem)_routedItemManager.Clone(); toCache.fromConnection = Connection.name; toCache.toConnections.Clear(); //BOUR-863 the toConnections on the toCache object weren't being cleared before rules so it contained DICOMConnection which toCache.attempts = 0; toCache.lastAttempt = DateTime.MinValue; toCache.type = RoutedItem.Type.RPC; _routedItemManager.Init(toCache); _routedItemManager.Enqueue(Connection, Connection.toRules, nameof(Connection.toRules)); break; } } catch (TaskCanceledException) { _logger.Log(LogLevel.Information, $"Task was canceled."); } catch (Exception e) { _logger.LogFullException(e, taskInfo); Dictionary <string, string> returnTagData = new Dictionary <string, string> { { "StatusCode", "-1" }, { "StatusDescription", $"Error: {e.Message}" }, { "StatusErrorComment", $"Error: {e.StackTrace}" } }; string key = "response"; Dictionary <string, Dictionary <string, string> > results = new Dictionary <string, Dictionary <string, string> > { { key, returnTagData } }; string jsonResults = JsonSerializer.Serialize(results); routedItem.response.Add(jsonResults); if (routedItem.attempts > Connection.maxAttempts) { routedItem.status = RoutedItem.Status.FAILED; routedItem.resultsTime = DateTime.Now; _logger.Log(LogLevel.Debug, $"{taskInfo} id: {routedItem.id} exceeded max attempts."); _routedItemManager.Init(routedItem); _routedItemManager.Dequeue(Connection, Connection.toFindSCU, nameof(Connection.toFindSCU), true); } routedItem.fromConnection = Connection.name; routedItem.toConnections.Clear(); routedItem.type = RoutedItem.Type.RPC; _routedItemManager.Init(routedItem); _routedItemManager.Enqueue(Connection, Connection.toRules, nameof(Connection.toRules)); return(routedItem); } return(routedItem); }
public void CFind(RoutedItem routedItem, DICOMConnection Connection, int taskID) { var taskInfo = $"task: {taskID} connection: {Connection.name}"; var stopWatch = new Stopwatch(); stopWatch.Start(); try { LiCloudRequest cFindParams = LiCloudRequest.FromJson(routedItem.request, _logger); foreach (var tag in cFindParams.searchTags) { _logger.Log(LogLevel.Debug, $"{taskInfo} id: {routedItem.id} tag: {tag.Key} {tag.Value}"); } DicomCFindRequest cFind = null; string cfindlevel = ""; cFindParams.searchTags.TryGetValue("0008,0052", out cfindlevel); switch (cfindlevel) { case "SERIES": cFind = new DicomCFindRequest(DicomQueryRetrieveLevel.Series); break; case "IMAGE": cFind = new DicomCFindRequest(DicomQueryRetrieveLevel.Image); break; case "PATIENT": cFind = new DicomCFindRequest(DicomQueryRetrieveLevel.Patient); break; case "WORKLIST": case "NA": cFind = new DicomCFindRequest(DicomQueryRetrieveLevel.NotApplicable); break; case "STUDY": default: cFind = new DicomCFindRequest(DicomQueryRetrieveLevel.Study); break; } _logger.Log(LogLevel.Information, $"{taskInfo} Request id: {routedItem.id} MessageID: {cFind.MessageID} attempt: {routedItem.attempts}"); //default return tags // Encoding encoding = DicomEncoding.GetEncoding(cFindCharacterSet); // if(cFindCharacterSet != null){ // cFind.Dataset.AddOrUpdate(DicomTag.SpecificCharacterSet, cFindCharacterSet); // } // cFind.Dataset.AddOrUpdate(DicomTag.PatientID, encoding, ""); // cFind.Dataset.AddOrUpdate(DicomTag.PatientName, encoding, ""); // cFind.Dataset.AddOrUpdate(DicomTag.PatientBirthDate, encoding, ""); // cFind.Dataset.AddOrUpdate(DicomTag.PatientSex, encoding, ""); // cFind.Dataset.AddOrUpdate(DicomTag.StudyDate, encoding, ""); // cFind.Dataset.AddOrUpdate(DicomTag.AccessionNumber, encoding, ""); // cFind.Dataset.AddOrUpdate(DicomTag.StudyID, encoding, ""); // cFind.Dataset.AddOrUpdate(DicomTag.ModalitiesInStudy, encoding, ""); // cFind.Dataset.AddOrUpdate(DicomTag.StudyInstanceUID, encoding, ""); // cFind.Dataset.AddOrUpdate(DicomTag.NumberOfStudyRelatedInstances, encoding, ""); // cFind.Dataset.AddOrUpdate(DicomTag.NumberOfSeriesRelatedInstances, encoding, ""); // cFind.Dataset.AddOrUpdate(DicomTag.StudyDescription, encoding, ""); // cFind.Dataset.AddOrUpdate(DicomTag.ModalitiesInStudy, encoding, ""); cFind.Dataset.AddOrUpdate(DicomTag.AccessionNumber, ""); cFind.Dataset.AddOrUpdate(DicomTag.NumberOfStudyRelatedInstances, ""); cFind.Dataset.AddOrUpdate(DicomTag.NumberOfSeriesRelatedInstances, ""); cFind.Dataset.AddOrUpdate(DicomTag.Modality, ""); cFind.Dataset.AddOrUpdate(DicomTag.ModalitiesInStudy, ""); cFind.Dataset.AddOrUpdate(DicomTag.PatientID, ""); cFind.Dataset.AddOrUpdate(DicomTag.PatientName, ""); cFind.Dataset.AddOrUpdate(DicomTag.PatientBirthDate, ""); cFind.Dataset.AddOrUpdate(DicomTag.PatientSex, ""); cFind.Dataset.AddOrUpdate(DicomTag.StudyDate, ""); cFind.Dataset.AddOrUpdate(DicomTag.StudyID, ""); cFind.Dataset.AddOrUpdate(DicomTag.StudyInstanceUID, ""); cFind.Dataset.AddOrUpdate(DicomTag.StudyDescription, ""); // add the search tags foreach (KeyValuePair <string, string> tag in cFindParams.searchTags) { try { // if(cFindCharacterSet != null){ // Encoding iso = DicomEncoding.GetEncoding(cFindCharacterSet); // Encoding utf8 = Encoding.UTF8; // byte[] utfBytes = utf8.GetBytes(tag.Value); // byte[] isoBytes = Encoding.Convert(utf8, iso, utfBytes); // string value = iso.GetString(isoBytes); // //cFind.Dataset.AddOrUpdate(DicomTag.Parse(tag.Key), value); // cFind.Dataset.AddOrUpdate(DicomTag.Parse(tag.Key), Encoding.ASCII, value); // } else { cFind.Dataset.AddOrUpdate(DicomTag.Parse(tag.Key), tag.Value); // } } catch (Exception e) { _logger.LogFullException(e, taskInfo); } } //connect the cFind to the RoutedItem that originated the request //the cache was already primed in GetRequests cFind.UserState = routedItem; routedItem.MessageId = cFind.MessageID; routedItem.fromConnection = Connection.name; routedItem.toConnections.Clear(); routedItem.status = RoutedItem.Status.PENDING; // var riToCache = (RoutedItem)routedItem.Clone(); // riToCache.Enqueue(this, toRules, nameof(toRules)); cFind.OnResponseReceived = (DicomCFindRequest request, DicomCFindResponse response) => { RoutedItem ri = (RoutedItem)request.UserState; _logger.Log(LogLevel.Information, $"{taskInfo} Response id: {ri.id} MessageID: {request.MessageID} status: {response.Status} elapsed: {stopWatch.Elapsed}"); //DICOMConnection dicomConn = LITE.profile.GetDicomConnectionToLocalAETitle(Connection.localAETitle); DICOMConnection dicomConn = _connectionFinder.GetDicomConnectionToLocalAETitle(_profileStorage.Current, Connection.localAETitle); Dictionary <string, string> returnTagData = new Dictionary <string, string> { { "StatusCode", response.Status.Code.ToString() }, { "StatusDescription", response.Status.Description }, { "StatusErrorComment", response.Status.ErrorComment }, { "StatusState", response.Status.State.ToString() } }; if (response.Completed != 0 || response.Remaining != 0) { returnTagData.Add("Completed", response.Completed.ToString()); returnTagData.Add("Remaining", response.Remaining.ToString()); } string key = "response"; Dictionary <string, Dictionary <string, string> > results = new Dictionary <string, Dictionary <string, string> >(); if (response.Dataset != null) { // Copy into a map, DicomDataset wont serialize foreach (var dicomItem in response.Dataset) { _logger.Log(LogLevel.Debug, $"{taskInfo} Response id: {ri.id} MessageID: {request.MessageID}, {dicomItem.Tag.ToString()} {response.Dataset.GetValueOrDefault<string>(dicomItem.Tag, 0, "")}"); if (dicomItem.Tag.ToString() == "(0008,0054)") //BOUR-940 mask AETitle with conn name { returnTagData.Add(dicomItem.Tag.ToString(), Connection.name); } else { returnTagData.Add(dicomItem.Tag.ToString(), response.Dataset.GetValueOrDefault <string>(dicomItem.Tag, 0, "")); } if (dicomItem.Tag == DicomTag.StudyInstanceUID) { key = response.Dataset.GetValueOrDefault <string>(dicomItem.Tag, 0, ""); } } results.Add(key, returnTagData); } if ((results.Count == 0 && ri.response.Count == 0) || (results.Count > 0)) { string jsonResults = JsonSerializer.Serialize(results); // set results in RoutedItem ri.response.Add(jsonResults); } switch (response.Status.ToString()) { case "Success": ri.status = RoutedItem.Status.COMPLETED; ri.resultsTime = DateTime.Now; _routedItemManager.Init(ri); _routedItemManager.Dequeue(Connection, Connection.toDicom, nameof(Connection.toDicom), error: false); //var toCache = (RoutedItem)ri.Clone(); var toCache = _routedItemManager.Clone(); toCache.fromConnection = Connection.name; toCache.toConnections.Clear(); //BOUR-863 the toConnections on the toCache object weren't being cleared before rules so it contained DICOMConnection which toCache.attempts = 0; toCache.lastAttempt = DateTime.MinValue; _routedItemManager.Init(toCache); _routedItemManager.Enqueue(Connection, Connection.toRules, nameof(Connection.toRules)); break; case "Pending": break; default: _routedItemManager.Init(ri); _routedItemManager.Dequeue(Connection, Connection.toDicom, nameof(Connection.toDicom), error: true); break; } }; dicomClient.AddRequest(cFind); } catch (DicomDataException e) { _logger.Log(LogLevel.Warning, $"{taskInfo} query: {routedItem} {e.Message} {e.StackTrace} "); routedItem.status = RoutedItem.Status.FAILED; routedItem.resultsTime = DateTime.Now; Dictionary <string, string> returnTagData = new Dictionary <string, string> { { "StatusCode", "-1" }, { "StatusDescription", $"Error: {e.Message}" }, { "StatusErrorComment", $"Error: {e.StackTrace}" }, { "StatusState", "" } }; string key = "response"; Dictionary <string, Dictionary <string, string> > results = new Dictionary <string, Dictionary <string, string> > { { key, returnTagData } }; string jsonResults = JsonSerializer.Serialize(results); routedItem.response.Add(jsonResults); _routedItemManager.Init(routedItem); _routedItemManager.Dequeue(Connection, Connection.toDicom, nameof(Connection.toDicom), true); routedItem.fromConnection = Connection.name; routedItem.toConnections.Clear(); _routedItemManager.Enqueue(Connection, Connection.toRules, nameof(Connection.toRules)); } catch (Exception e) { _logger.LogFullException(e, taskInfo); Dictionary <string, string> returnTagData = new Dictionary <string, string> { { "StatusCode", "-1" }, { "StatusDescription", $"Error: {e.Message}" }, { "StatusErrorComment", $"Error: {e.StackTrace}" }, { "StatusState", "" } }; string key = "response"; Dictionary <string, Dictionary <string, string> > results = new Dictionary <string, Dictionary <string, string> > { { key, returnTagData } }; string jsonResults = JsonSerializer.Serialize(results); routedItem.response.Add(jsonResults); if (routedItem.attempts > Connection.maxAttempts) { routedItem.status = RoutedItem.Status.FAILED; routedItem.resultsTime = DateTime.Now; _logger.Log(LogLevel.Debug, $"{taskInfo} id: {routedItem.id} exceeded max attempts."); _routedItemManager.Init(routedItem); _routedItemManager.Dequeue(Connection, Connection.toDicom, nameof(Connection.toDicom), true); } routedItem.fromConnection = Connection.name; routedItem.toConnections.Clear(); _routedItemManager.Init(routedItem); _routedItemManager.Enqueue(Connection, Connection.toRules, nameof(Connection.toRules)); } }