WorklistService.cs 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248
  1. // Copyright (c) 2012-2020 fo-dicom contributors.
  2. // Licensed under the Microsoft Public License (MS-PL).
  3. using System;
  4. using System.Collections.Generic;
  5. using System.Linq;
  6. using System.Text;
  7. using System.Threading.Tasks;
  8. using Dicom;
  9. using Dicom.Log;
  10. using Dicom.Network;
  11. using Worklist_SCP.Model;
  12. using System.Configuration;
  13. namespace Worklist_SCP
  14. {
  15. public class WorklistService : DicomService, IDicomServiceProvider, IDicomCEchoProvider, IDicomCFindProvider, IDicomNServiceProvider
  16. {
  17. private static readonly DicomTransferSyntax[] AcceptedTransferSyntaxes = new DicomTransferSyntax[]
  18. {
  19. DicomTransferSyntax.ExplicitVRLittleEndian,
  20. DicomTransferSyntax.ExplicitVRBigEndian,
  21. DicomTransferSyntax.ImplicitVRLittleEndian
  22. };
  23. private IMppsSource _mppsSource;
  24. private IMppsSource MppsSource
  25. {
  26. get
  27. {
  28. if (_mppsSource == null)
  29. {
  30. _mppsSource = new MppsHandler(Logger);
  31. }
  32. return _mppsSource;
  33. }
  34. }
  35. public WorklistService(INetworkStream stream, Encoding fallbackEncoding, Logger log) : base(stream, fallbackEncoding, log)
  36. {
  37. }
  38. public DicomCEchoResponse OnCEchoRequest(DicomCEchoRequest request)
  39. {
  40. Logger.Info($"Received verification request from AE {Association.CallingAE} with IP: {Association.RemoteHost}");
  41. return new DicomCEchoResponse(request, DicomStatus.Success);
  42. }
  43. public IEnumerable<DicomCFindResponse> OnCFindRequest(DicomCFindRequest request)
  44. {
  45. // you should validate the level of the request. I leave it here since there is a bug in version 3.0.2
  46. // from version 4 on this should be done
  47. //if (request.Level != DicomQueryRetrieveLevel.Worklist)
  48. //{
  49. // yield return new DicomCFindResponse(request, DicomStatus.QueryRetrieveUnableToProcess);
  50. //}
  51. //else
  52. //{
  53. var configType = ConfigurationManager.AppSettings["Type"];
  54. IEnumerable<DicomDataset> exams = null;
  55. Console.Write($"configType: {configType}");
  56. switch (configType)
  57. {
  58. case "API":
  59. exams = WorklistApiHandler.FilterWorklistItems(request.Dataset);
  60. break;
  61. case "JSON":
  62. exams = WorklistJsonHandler.FilterWorklistItems(request.Dataset);
  63. break;
  64. case "DB":
  65. break;
  66. default:
  67. exams = WorklistHandler.FilterWorklistItems(request.Dataset, new WorklistItemsProvider().GetAllCurrentWorklistItems());
  68. break;
  69. }
  70. //var exams = WorklistApiHandler.FilterWorklistItems(request.Dataset, WorklistServer.CurrentWorklistItems);
  71. foreach (DicomDataset result in exams)
  72. //foreach (DicomDataset result in WorklistHandler.FilterWorklistItems(request.Dataset, WorklistServer.CurrentWorklistItems))
  73. {
  74. yield return new DicomCFindResponse(request, DicomStatus.Pending) { Dataset = result };
  75. }
  76. yield return new DicomCFindResponse(request, DicomStatus.Success);
  77. //}
  78. }
  79. public void OnConnectionClosed(Exception exception)
  80. {
  81. Clean();
  82. }
  83. public void OnReceiveAbort(DicomAbortSource source, DicomAbortReason reason)
  84. {
  85. //log the abort reason
  86. Logger.Error($"Received abort from {source}, reason is {reason}");
  87. }
  88. public Task OnReceiveAssociationReleaseRequestAsync()
  89. {
  90. Clean();
  91. return SendAssociationReleaseResponseAsync();
  92. }
  93. public Task OnReceiveAssociationRequestAsync(DicomAssociation association)
  94. {
  95. Logger.Info($"Received association request from AE: {association.CallingAE} with IP: {association.RemoteHost} ");
  96. //if (WorklistServer.AETitle != association.CalledAE)
  97. //{
  98. // Logger.Error($"Association with {association.CallingAE} rejected since called aet {association.CalledAE} is unknown");
  99. // return SendAssociationRejectAsync(DicomRejectResult.Permanent, DicomRejectSource.ServiceUser, DicomRejectReason.CalledAENotRecognized);
  100. //}
  101. foreach (var pc in association.PresentationContexts)
  102. {
  103. if (pc.AbstractSyntax == DicomUID.Verification
  104. || pc.AbstractSyntax == DicomUID.ModalityWorklistInformationModelFIND
  105. || pc.AbstractSyntax == DicomUID.ModalityPerformedProcedureStepSOPClass
  106. || pc.AbstractSyntax == DicomUID.ModalityPerformedProcedureStepNotificationSOPClass
  107. || pc.AbstractSyntax == DicomUID.ModalityPerformedProcedureStepNotificationSOPClass)
  108. {
  109. pc.AcceptTransferSyntaxes(AcceptedTransferSyntaxes);
  110. }
  111. else
  112. {
  113. Logger.Warn($"Requested abstract syntax {pc.AbstractSyntax} from {association.CallingAE} not supported");
  114. pc.SetResult(DicomPresentationContextResult.RejectAbstractSyntaxNotSupported);
  115. }
  116. }
  117. Logger.Info($"Accepted association request from {association.CallingAE}");
  118. return SendAssociationAcceptAsync(association);
  119. }
  120. public void Clean()
  121. {
  122. // cleanup, like cancel outstanding move- or get-jobs
  123. }
  124. public DicomNCreateResponse OnNCreateRequest(DicomNCreateRequest request)
  125. {
  126. if (request.SOPClassUID != DicomUID.ModalityPerformedProcedureStepSOPClass)
  127. {
  128. return new DicomNCreateResponse(request, DicomStatus.SOPClassNotSupported);
  129. }
  130. // on N-Create the UID is stored in AffectedSopInstanceUID, in N-Set the UID is stored in RequestedSopInstanceUID
  131. var affectedSopInstanceUID = request.Command.GetSingleValue<string>(DicomTag.AffectedSOPInstanceUID);
  132. Logger.Log(LogLevel.Info, $"reeiving N-Create with SOPUID {affectedSopInstanceUID}");
  133. // get the procedureStepIds from the request
  134. var procedureStepId = request.Dataset
  135. .GetSequence(DicomTag.ScheduledStepAttributesSequence)
  136. .First()
  137. .GetSingleValue<string>(DicomTag.ScheduledProcedureStepID);
  138. var ok = MppsSource.SetInProgress(affectedSopInstanceUID, procedureStepId);
  139. return new DicomNCreateResponse(request, ok ? DicomStatus.Success : DicomStatus.ProcessingFailure);
  140. }
  141. public DicomNSetResponse OnNSetRequest(DicomNSetRequest request)
  142. {
  143. if (request.SOPClassUID != DicomUID.ModalityPerformedProcedureStepSOPClass)
  144. {
  145. return new DicomNSetResponse(request, DicomStatus.SOPClassNotSupported);
  146. }
  147. // on N-Create the UID is stored in AffectedSopInstanceUID, in N-Set the UID is stored in RequestedSopInstanceUID
  148. var requestedSopInstanceUID = request.Command.GetSingleValue<string>(DicomTag.RequestedSOPInstanceUID);
  149. Logger.Log(LogLevel.Info, $"receiving N-Set with SOPUID {requestedSopInstanceUID}");
  150. var status = request.Dataset.GetSingleValue<string>(DicomTag.PerformedProcedureStepStatus);
  151. if (status == "COMPLETED")
  152. {
  153. // most vendors send some informations with the mpps-completed message.
  154. // this information should be stored into the datbase
  155. var doseDescription = request.Dataset.GetSingleValueOrDefault(DicomTag.CommentsOnRadiationDose, string.Empty);
  156. var listOfInstanceUIDs = new List<string>();
  157. foreach (var seriesDataset in request.Dataset.GetSequence(DicomTag.PerformedSeriesSequence))
  158. {
  159. // you can read here some information about the series that the modalidy created
  160. //seriesDataset.Get(DicomTag.SeriesDescription, string.Empty);
  161. //seriesDataset.Get(DicomTag.PerformingPhysicianName, string.Empty);
  162. //seriesDataset.Get(DicomTag.ProtocolName, string.Empty);
  163. foreach (var instanceDataset in seriesDataset.GetSequence(DicomTag.ReferencedImageSequence))
  164. {
  165. // here you can read the SOPClassUID and SOPInstanceUID
  166. var instanceUID = instanceDataset.GetSingleValueOrDefault(DicomTag.ReferencedSOPInstanceUID, string.Empty);
  167. if (!string.IsNullOrEmpty(instanceUID)) listOfInstanceUIDs.Add(instanceUID);
  168. }
  169. }
  170. var ok = MppsSource.SetCompleted(requestedSopInstanceUID, doseDescription, listOfInstanceUIDs);
  171. return new DicomNSetResponse(request, ok ? DicomStatus.Success : DicomStatus.ProcessingFailure);
  172. }
  173. else if (status == "DISCONTINUED")
  174. {
  175. // some vendors send a reason code or description with the mpps-discontinued message
  176. // var reason = request.Dataset.Get(DicomTag.PerformedProcedureStepDiscontinuationReasonCodeSequence);
  177. var ok = MppsSource.SetDiscontinued(requestedSopInstanceUID, string.Empty);
  178. return new DicomNSetResponse(request, ok ? DicomStatus.Success : DicomStatus.ProcessingFailure);
  179. }
  180. else
  181. {
  182. return new DicomNSetResponse(request, DicomStatus.InvalidAttributeValue);
  183. }
  184. }
  185. #region not supported methods but that are required because of the interface
  186. public DicomNDeleteResponse OnNDeleteRequest(DicomNDeleteRequest request)
  187. {
  188. Logger.Log(LogLevel.Info, "receiving N-Delete, not supported");
  189. return new DicomNDeleteResponse(request, DicomStatus.UnrecognizedOperation);
  190. }
  191. public DicomNEventReportResponse OnNEventReportRequest(DicomNEventReportRequest request)
  192. {
  193. Logger.Log(LogLevel.Info, "receiving N-Event, not supported");
  194. return new DicomNEventReportResponse(request, DicomStatus.UnrecognizedOperation);
  195. }
  196. public DicomNGetResponse OnNGetRequest(DicomNGetRequest request)
  197. {
  198. Logger.Log(LogLevel.Info, "receiving N-Get, not supported");
  199. return new DicomNGetResponse(request, DicomStatus.UnrecognizedOperation);
  200. }
  201. public DicomNActionResponse OnNActionRequest(DicomNActionRequest request)
  202. {
  203. Logger.Log(LogLevel.Info, "receiving N-Action, not supported");
  204. return new DicomNActionResponse(request, DicomStatus.UnrecognizedOperation);
  205. }
  206. #endregion
  207. }
  208. }