Program.cs 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246
  1. // Copyright (c) 2012-2020 fo-dicom contributors.
  2. // Licensed under the Microsoft Public License (MS-PL).
  3. using Dicom;
  4. using Dicom.Network;
  5. using System;
  6. using System.Collections.Generic;
  7. using System.IO;
  8. using System.Linq;
  9. using System.Threading.Tasks;
  10. using DicomClient = Dicom.Network.Client.DicomClient;
  11. namespace QueryRetrieve_SCU
  12. {
  13. internal static class Program
  14. {
  15. private static readonly string StoragePath = @".\DICOM";
  16. // values of the Query Retrieve Server to test with.
  17. private static readonly string QRServerHost = "localhost"; // "www.dicomserver.co.uk";
  18. private static readonly int QRServerPort = 8001; // 104;
  19. private static readonly string QRServerAET = "QRSCP"; // "STORESCP";
  20. private static readonly string AET = "FODICOMSCU";
  21. static async Task Main(string[] args)
  22. {
  23. var client = new DicomClient(QRServerHost, QRServerPort, false, AET, QRServerAET);
  24. client.NegotiateAsyncOps();
  25. // Find a list of Studies
  26. var request = CreateStudyRequestByPatientName("Traxler^Y*");
  27. var studyUids = new List<string>();
  28. request.OnResponseReceived += (req, response) =>
  29. {
  30. DebugStudyResponse(response);
  31. studyUids.Add(response.Dataset?.GetSingleValue<string>(DicomTag.StudyInstanceUID));
  32. };
  33. await client.AddRequestAsync(request);
  34. await client.SendAsync();
  35. // find all series from a study that previous was returned
  36. var studyUID = studyUids[0];
  37. request = CreateSeriesRequestByStudyUID(studyUID);
  38. var serieUids = new List<string>();
  39. request.OnResponseReceived += (req, response) =>
  40. {
  41. DebugSerieResponse(response);
  42. serieUids.Add(response.Dataset?.GetSingleValue<string>(DicomTag.SeriesInstanceUID));
  43. };
  44. await client.AddRequestAsync(request);
  45. await client.SendAsync();
  46. // now get all the images of a serie with cGet in the same association
  47. client = new DicomClient(QRServerHost, QRServerPort, false, AET, QRServerAET);
  48. var cGetRequest = CreateCGetBySeriesUID(studyUID, serieUids.First());
  49. client.OnCStoreRequest += (DicomCStoreRequest req) =>
  50. {
  51. Console.WriteLine(DateTime.Now.ToString() + " recived");
  52. SaveImage(req.Dataset);
  53. return Task.FromResult(new DicomCStoreResponse(req, DicomStatus.Success));
  54. };
  55. // the client has to accept storage of the images. We know that the requested images are of SOP class Secondary capture,
  56. // so we add the Secondary capture to the additional presentation context
  57. // a more general approach would be to mace a cfind-request on image level and to read a list of distinct SOP classes of all
  58. // the images. these SOP classes shall be added here.
  59. var pcs = DicomPresentationContext.GetScpRolePresentationContextsFromStorageUids(
  60. DicomStorageCategory.Image,
  61. DicomTransferSyntax.ExplicitVRLittleEndian,
  62. DicomTransferSyntax.ImplicitVRLittleEndian,
  63. DicomTransferSyntax.ImplicitVRBigEndian);
  64. client.AdditionalPresentationContexts.AddRange(pcs);
  65. await client.AddRequestAsync(cGetRequest);
  66. await client.SendAsync();
  67. // if the images shall be sent to an existing storescp and this storescp is configured on the QR SCP then a CMove could be performed:
  68. // here we want to see how a error case looks like - because the test QR Server does not know the node FODICOMSCP
  69. client = new DicomClient(QRServerHost, QRServerPort, false, AET, QRServerAET);
  70. var cMoveRequest = CreateCMoveByStudyUID("STORESCP", studyUID);
  71. bool? moveSuccessfully = null;
  72. cMoveRequest.OnResponseReceived += (DicomCMoveRequest requ, DicomCMoveResponse response) =>
  73. {
  74. if (response.Status.State == DicomState.Pending)
  75. {
  76. Console.WriteLine("Sending is in progress. please wait: " + response.Remaining.ToString());
  77. }
  78. else if (response.Status.State == DicomState.Success)
  79. {
  80. Console.WriteLine("Sending successfully finished");
  81. moveSuccessfully = true;
  82. }
  83. else if (response.Status.State == DicomState.Failure)
  84. {
  85. Console.WriteLine("Error sending datasets: " + response.Status.Description);
  86. moveSuccessfully = false;
  87. }
  88. Console.WriteLine(response.Status);
  89. };
  90. await client.AddRequestAsync(cMoveRequest);
  91. await client.SendAsync();
  92. if (moveSuccessfully.GetValueOrDefault(false))
  93. {
  94. Console.WriteLine("images sent successfully");
  95. // images sent successfully from QR Server to the store scp
  96. }
  97. Console.ReadLine();
  98. }
  99. public static DicomCFindRequest CreateStudyRequestByPatientName(string patientName)
  100. {
  101. // there is a built in function to create a Study-level CFind request very easily:
  102. // return DicomCFindRequest.CreateStudyQuery(patientName: patientName);
  103. // but consider to create your own request that contains exactly those DicomTags that
  104. // you realy need pro process your data and not to cause unneccessary traffic and IO load:
  105. var request = new DicomCFindRequest(DicomQueryRetrieveLevel.Study);
  106. // always add the encoding
  107. request.Dataset.AddOrUpdate(new DicomTag(0x8, 0x5), "ISO_IR 100");
  108. // add the dicom tags with empty values that should be included in the result of the QR Server
  109. request.Dataset.AddOrUpdate(DicomTag.PatientName, "");
  110. request.Dataset.AddOrUpdate(DicomTag.PatientID, "");
  111. request.Dataset.AddOrUpdate(DicomTag.ModalitiesInStudy, "");
  112. request.Dataset.AddOrUpdate(DicomTag.StudyDate, "");
  113. request.Dataset.AddOrUpdate(DicomTag.StudyInstanceUID, "");
  114. request.Dataset.AddOrUpdate(DicomTag.StudyDescription, "");
  115. // add the dicom tags that contain the filter criterias
  116. request.Dataset.AddOrUpdate(DicomTag.PatientName, patientName);
  117. return request;
  118. }
  119. public static DicomCFindRequest CreateSeriesRequestByStudyUID(string studyInstanceUID)
  120. {
  121. // there is a built in function to create a Study-level CFind request very easily:
  122. // return DicomCFindRequest.CreateSeriesQuery(studyInstanceUID);
  123. // but consider to create your own request that contains exactly those DicomTags that
  124. // you realy need pro process your data and not to cause unneccessary traffic and IO load:
  125. var request = new DicomCFindRequest(DicomQueryRetrieveLevel.Series);
  126. request.Dataset.AddOrUpdate(new DicomTag(0x8, 0x5), "ISO_IR 100");
  127. // add the dicom tags with empty values that should be included in the result
  128. request.Dataset.AddOrUpdate(DicomTag.SeriesInstanceUID, "");
  129. request.Dataset.AddOrUpdate(DicomTag.SeriesDescription, "");
  130. request.Dataset.AddOrUpdate(DicomTag.Modality, "");
  131. request.Dataset.AddOrUpdate(DicomTag.NumberOfSeriesRelatedInstances, "");
  132. // add the dicom tags that contain the filter criterias
  133. request.Dataset.AddOrUpdate(DicomTag.StudyInstanceUID, studyInstanceUID);
  134. return request;
  135. }
  136. public static DicomCGetRequest CreateCGetBySeriesUID(string studyUID, string seriesUID)
  137. {
  138. var request = new DicomCGetRequest(studyUID, seriesUID);
  139. // no more dicomtags have to be set
  140. return request;
  141. }
  142. public static DicomCMoveRequest CreateCMoveBySeriesUID(string destination, string studyUID, string seriesUID)
  143. {
  144. var request = new DicomCMoveRequest(destination, studyUID, seriesUID);
  145. // no more dicomtags have to be set
  146. return request;
  147. }
  148. public static DicomCMoveRequest CreateCMoveByStudyUID(string destination, string studyUID)
  149. {
  150. var request = new DicomCMoveRequest(destination, studyUID);
  151. // no more dicomtags have to be set
  152. return request;
  153. }
  154. public static void DebugStudyResponse(DicomCFindResponse response)
  155. {
  156. if (response.Status == DicomStatus.Pending)
  157. {
  158. // print the results
  159. Console.WriteLine($"Patient {response.Dataset.GetSingleValueOrDefault(DicomTag.PatientName, string.Empty)}, {(response.Dataset.TryGetString(DicomTag.ModalitiesInStudy, out var dummy) ? dummy : string.Empty)}-Study from {response.Dataset.GetSingleValueOrDefault(DicomTag.StudyDate, new DateTime())} with UID {response.Dataset.GetSingleValueOrDefault(DicomTag.StudyInstanceUID, string.Empty)} ");
  160. }
  161. if (response.Status == DicomStatus.Success)
  162. {
  163. Console.WriteLine(response.Status.ToString());
  164. }
  165. }
  166. public static void DebugSerieResponse(DicomCFindResponse response)
  167. {
  168. try
  169. {
  170. if (response.Status == DicomStatus.Pending)
  171. {
  172. // print the results
  173. Console.WriteLine($"Serie {response.Dataset.GetSingleValue<string>(DicomTag.SeriesDescription)}, {response.Dataset.GetSingleValue<string>(DicomTag.Modality)}, {response.Dataset.GetSingleValue<int>(DicomTag.NumberOfSeriesRelatedInstances)} instances");
  174. }
  175. if (response.Status == DicomStatus.Success)
  176. {
  177. Console.WriteLine(response.Status.ToString());
  178. }
  179. }
  180. catch (Exception)
  181. {
  182. // ignore errors
  183. }
  184. }
  185. public static void SaveImage(DicomDataset dataset)
  186. {
  187. var studyUid = dataset.GetSingleValue<string>(DicomTag.StudyInstanceUID);
  188. var instUid = dataset.GetSingleValue<string>(DicomTag.SOPInstanceUID);
  189. var path = Path.GetFullPath(StoragePath);
  190. path = Path.Combine(path, studyUid);
  191. if (!Directory.Exists(path)) Directory.CreateDirectory(path);
  192. path = Path.Combine(path, instUid) + ".dcm";
  193. new DicomFile(dataset).Save(path);
  194. }
  195. }
  196. }