WorklistService.cs 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254
  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.ModalityPerformedProcedureStep
  106. || pc.AbstractSyntax == DicomUID.ModalityPerformedProcedureStepNotification
  107. || pc.AbstractSyntax == DicomUID.ModalityPerformedProcedureStepNotification)
  108. // || pc.AbstractSyntax == DicomUID.ModalityWorklistInformationModelFIND
  109. // || pc.AbstractSyntax == DicomUID.ModalityPerformedProcedureStepSOPClass
  110. // || pc.AbstractSyntax == DicomUID.ModalityPerformedProcedureStepNotificationSOPClass
  111. // || pc.AbstractSyntax == DicomUID.ModalityPerformedProcedureStepNotificationSOPClass)
  112. {
  113. pc.AcceptTransferSyntaxes(AcceptedTransferSyntaxes);
  114. }
  115. else
  116. {
  117. Logger.Warn($"Requested abstract syntax {pc.AbstractSyntax} from {association.CallingAE} not supported");
  118. pc.SetResult(DicomPresentationContextResult.RejectAbstractSyntaxNotSupported);
  119. }
  120. }
  121. Logger.Info($"Accepted association request from {association.CallingAE}");
  122. return SendAssociationAcceptAsync(association);
  123. }
  124. public void Clean()
  125. {
  126. // cleanup, like cancel outstanding move- or get-jobs
  127. }
  128. public DicomNCreateResponse OnNCreateRequest(DicomNCreateRequest request)
  129. {
  130. // if (request.SOPClassUID != DicomUID.ModalityPerformedProcedureStepSOPClass)
  131. if (request.SOPClassUID != DicomUID.ModalityPerformedProcedureStep)
  132. {
  133. return new DicomNCreateResponse(request, DicomStatus.SOPClassNotSupported);
  134. }
  135. // on N-Create the UID is stored in AffectedSopInstanceUID, in N-Set the UID is stored in RequestedSopInstanceUID
  136. var affectedSopInstanceUID = request.Command.GetSingleValue<string>(DicomTag.AffectedSOPInstanceUID);
  137. Logger.Log(LogLevel.Info, $"reeiving N-Create with SOPUID {affectedSopInstanceUID}");
  138. // get the procedureStepIds from the request
  139. var procedureStepId = request.Dataset
  140. .GetSequence(DicomTag.ScheduledStepAttributesSequence)
  141. .First()
  142. .GetSingleValue<string>(DicomTag.ScheduledProcedureStepID);
  143. var ok = MppsSource.SetInProgress(affectedSopInstanceUID, procedureStepId);
  144. return new DicomNCreateResponse(request, ok ? DicomStatus.Success : DicomStatus.ProcessingFailure);
  145. }
  146. public DicomNSetResponse OnNSetRequest(DicomNSetRequest request)
  147. {
  148. // if (request.SOPClassUID != DicomUID.ModalityPerformedProcedureStepSOPClass)
  149. if (request.SOPClassUID != DicomUID.ModalityPerformedProcedureStep)
  150. {
  151. return new DicomNSetResponse(request, DicomStatus.SOPClassNotSupported);
  152. }
  153. // on N-Create the UID is stored in AffectedSopInstanceUID, in N-Set the UID is stored in RequestedSopInstanceUID
  154. var requestedSopInstanceUID = request.Command.GetSingleValue<string>(DicomTag.RequestedSOPInstanceUID);
  155. Logger.Log(LogLevel.Info, $"receiving N-Set with SOPUID {requestedSopInstanceUID}");
  156. var status = request.Dataset.GetSingleValue<string>(DicomTag.PerformedProcedureStepStatus);
  157. if (status == "COMPLETED")
  158. {
  159. // most vendors send some informations with the mpps-completed message.
  160. // this information should be stored into the datbase
  161. var doseDescription = request.Dataset.GetSingleValueOrDefault(DicomTag.CommentsOnRadiationDose, string.Empty);
  162. var listOfInstanceUIDs = new List<string>();
  163. foreach (var seriesDataset in request.Dataset.GetSequence(DicomTag.PerformedSeriesSequence))
  164. {
  165. // you can read here some information about the series that the modalidy created
  166. //seriesDataset.Get(DicomTag.SeriesDescription, string.Empty);
  167. //seriesDataset.Get(DicomTag.PerformingPhysicianName, string.Empty);
  168. //seriesDataset.Get(DicomTag.ProtocolName, string.Empty);
  169. foreach (var instanceDataset in seriesDataset.GetSequence(DicomTag.ReferencedImageSequence))
  170. {
  171. // here you can read the SOPClassUID and SOPInstanceUID
  172. var instanceUID = instanceDataset.GetSingleValueOrDefault(DicomTag.ReferencedSOPInstanceUID, string.Empty);
  173. if (!string.IsNullOrEmpty(instanceUID)) listOfInstanceUIDs.Add(instanceUID);
  174. }
  175. }
  176. var ok = MppsSource.SetCompleted(requestedSopInstanceUID, doseDescription, listOfInstanceUIDs);
  177. return new DicomNSetResponse(request, ok ? DicomStatus.Success : DicomStatus.ProcessingFailure);
  178. }
  179. else if (status == "DISCONTINUED")
  180. {
  181. // some vendors send a reason code or description with the mpps-discontinued message
  182. // var reason = request.Dataset.Get(DicomTag.PerformedProcedureStepDiscontinuationReasonCodeSequence);
  183. var ok = MppsSource.SetDiscontinued(requestedSopInstanceUID, string.Empty);
  184. return new DicomNSetResponse(request, ok ? DicomStatus.Success : DicomStatus.ProcessingFailure);
  185. }
  186. else
  187. {
  188. return new DicomNSetResponse(request, DicomStatus.InvalidAttributeValue);
  189. }
  190. }
  191. #region not supported methods but that are required because of the interface
  192. public DicomNDeleteResponse OnNDeleteRequest(DicomNDeleteRequest request)
  193. {
  194. Logger.Log(LogLevel.Info, "receiving N-Delete, not supported");
  195. return new DicomNDeleteResponse(request, DicomStatus.UnrecognizedOperation);
  196. }
  197. public DicomNEventReportResponse OnNEventReportRequest(DicomNEventReportRequest request)
  198. {
  199. Logger.Log(LogLevel.Info, "receiving N-Event, not supported");
  200. return new DicomNEventReportResponse(request, DicomStatus.UnrecognizedOperation);
  201. }
  202. public DicomNGetResponse OnNGetRequest(DicomNGetRequest request)
  203. {
  204. Logger.Log(LogLevel.Info, "receiving N-Get, not supported");
  205. return new DicomNGetResponse(request, DicomStatus.UnrecognizedOperation);
  206. }
  207. public DicomNActionResponse OnNActionRequest(DicomNActionRequest request)
  208. {
  209. Logger.Log(LogLevel.Info, "receiving N-Action, not supported");
  210. return new DicomNActionResponse(request, DicomStatus.UnrecognizedOperation);
  211. }
  212. #endregion
  213. }
  214. }