WorklistService.cs 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251
  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 "SQLServer":
  65. exams = WorklistSQLServerHandler.FilterWorklistItems(request.Dataset);
  66. break;
  67. case "DB":
  68. break;
  69. default:
  70. exams = WorklistHandler.FilterWorklistItems(request.Dataset, new WorklistItemsProvider().GetAllCurrentWorklistItems());
  71. break;
  72. }
  73. //var exams = WorklistApiHandler.FilterWorklistItems(request.Dataset, WorklistServer.CurrentWorklistItems);
  74. foreach (DicomDataset result in exams)
  75. //foreach (DicomDataset result in WorklistHandler.FilterWorklistItems(request.Dataset, WorklistServer.CurrentWorklistItems))
  76. {
  77. yield return new DicomCFindResponse(request, DicomStatus.Pending) { Dataset = result };
  78. }
  79. yield return new DicomCFindResponse(request, DicomStatus.Success);
  80. //}
  81. }
  82. public void OnConnectionClosed(Exception exception)
  83. {
  84. Clean();
  85. }
  86. public void OnReceiveAbort(DicomAbortSource source, DicomAbortReason reason)
  87. {
  88. //log the abort reason
  89. Logger.Error($"Received abort from {source}, reason is {reason}");
  90. }
  91. public Task OnReceiveAssociationReleaseRequestAsync()
  92. {
  93. Clean();
  94. return SendAssociationReleaseResponseAsync();
  95. }
  96. public Task OnReceiveAssociationRequestAsync(DicomAssociation association)
  97. {
  98. Logger.Info($"Received association request from AE: {association.CallingAE} with IP: {association.RemoteHost} ");
  99. //if (WorklistServer.AETitle != association.CalledAE)
  100. //{
  101. // Logger.Error($"Association with {association.CallingAE} rejected since called aet {association.CalledAE} is unknown");
  102. // return SendAssociationRejectAsync(DicomRejectResult.Permanent, DicomRejectSource.ServiceUser, DicomRejectReason.CalledAENotRecognized);
  103. //}
  104. foreach (var pc in association.PresentationContexts)
  105. {
  106. if (pc.AbstractSyntax == DicomUID.Verification
  107. || pc.AbstractSyntax == DicomUID.ModalityWorklistInformationModelFIND
  108. || pc.AbstractSyntax == DicomUID.ModalityPerformedProcedureStepSOPClass
  109. || pc.AbstractSyntax == DicomUID.ModalityPerformedProcedureStepNotificationSOPClass
  110. || pc.AbstractSyntax == DicomUID.ModalityPerformedProcedureStepNotificationSOPClass)
  111. {
  112. pc.AcceptTransferSyntaxes(AcceptedTransferSyntaxes);
  113. }
  114. else
  115. {
  116. Logger.Warn($"Requested abstract syntax {pc.AbstractSyntax} from {association.CallingAE} not supported");
  117. pc.SetResult(DicomPresentationContextResult.RejectAbstractSyntaxNotSupported);
  118. }
  119. }
  120. Logger.Info($"Accepted association request from {association.CallingAE}");
  121. return SendAssociationAcceptAsync(association);
  122. }
  123. public void Clean()
  124. {
  125. // cleanup, like cancel outstanding move- or get-jobs
  126. }
  127. public DicomNCreateResponse OnNCreateRequest(DicomNCreateRequest request)
  128. {
  129. if (request.SOPClassUID != DicomUID.ModalityPerformedProcedureStepSOPClass)
  130. {
  131. return new DicomNCreateResponse(request, DicomStatus.SOPClassNotSupported);
  132. }
  133. // on N-Create the UID is stored in AffectedSopInstanceUID, in N-Set the UID is stored in RequestedSopInstanceUID
  134. var affectedSopInstanceUID = request.Command.GetSingleValue<string>(DicomTag.AffectedSOPInstanceUID);
  135. Logger.Log(LogLevel.Info, $"reeiving N-Create with SOPUID {affectedSopInstanceUID}");
  136. // get the procedureStepIds from the request
  137. var procedureStepId = request.Dataset
  138. .GetSequence(DicomTag.ScheduledStepAttributesSequence)
  139. .First()
  140. .GetSingleValue<string>(DicomTag.ScheduledProcedureStepID);
  141. var ok = MppsSource.SetInProgress(affectedSopInstanceUID, procedureStepId);
  142. return new DicomNCreateResponse(request, ok ? DicomStatus.Success : DicomStatus.ProcessingFailure);
  143. }
  144. public DicomNSetResponse OnNSetRequest(DicomNSetRequest request)
  145. {
  146. if (request.SOPClassUID != DicomUID.ModalityPerformedProcedureStepSOPClass)
  147. {
  148. return new DicomNSetResponse(request, DicomStatus.SOPClassNotSupported);
  149. }
  150. // on N-Create the UID is stored in AffectedSopInstanceUID, in N-Set the UID is stored in RequestedSopInstanceUID
  151. var requestedSopInstanceUID = request.Command.GetSingleValue<string>(DicomTag.RequestedSOPInstanceUID);
  152. Logger.Log(LogLevel.Info, $"receiving N-Set with SOPUID {requestedSopInstanceUID}");
  153. var status = request.Dataset.GetSingleValue<string>(DicomTag.PerformedProcedureStepStatus);
  154. if (status == "COMPLETED")
  155. {
  156. // most vendors send some informations with the mpps-completed message.
  157. // this information should be stored into the datbase
  158. var doseDescription = request.Dataset.GetSingleValueOrDefault(DicomTag.CommentsOnRadiationDose, string.Empty);
  159. var listOfInstanceUIDs = new List<string>();
  160. foreach (var seriesDataset in request.Dataset.GetSequence(DicomTag.PerformedSeriesSequence))
  161. {
  162. // you can read here some information about the series that the modalidy created
  163. //seriesDataset.Get(DicomTag.SeriesDescription, string.Empty);
  164. //seriesDataset.Get(DicomTag.PerformingPhysicianName, string.Empty);
  165. //seriesDataset.Get(DicomTag.ProtocolName, string.Empty);
  166. foreach (var instanceDataset in seriesDataset.GetSequence(DicomTag.ReferencedImageSequence))
  167. {
  168. // here you can read the SOPClassUID and SOPInstanceUID
  169. var instanceUID = instanceDataset.GetSingleValueOrDefault(DicomTag.ReferencedSOPInstanceUID, string.Empty);
  170. if (!string.IsNullOrEmpty(instanceUID)) listOfInstanceUIDs.Add(instanceUID);
  171. }
  172. }
  173. var ok = MppsSource.SetCompleted(requestedSopInstanceUID, doseDescription, listOfInstanceUIDs);
  174. return new DicomNSetResponse(request, ok ? DicomStatus.Success : DicomStatus.ProcessingFailure);
  175. }
  176. else if (status == "DISCONTINUED")
  177. {
  178. // some vendors send a reason code or description with the mpps-discontinued message
  179. // var reason = request.Dataset.Get(DicomTag.PerformedProcedureStepDiscontinuationReasonCodeSequence);
  180. var ok = MppsSource.SetDiscontinued(requestedSopInstanceUID, string.Empty);
  181. return new DicomNSetResponse(request, ok ? DicomStatus.Success : DicomStatus.ProcessingFailure);
  182. }
  183. else
  184. {
  185. return new DicomNSetResponse(request, DicomStatus.InvalidAttributeValue);
  186. }
  187. }
  188. #region not supported methods but that are required because of the interface
  189. public DicomNDeleteResponse OnNDeleteRequest(DicomNDeleteRequest request)
  190. {
  191. Logger.Log(LogLevel.Info, "receiving N-Delete, not supported");
  192. return new DicomNDeleteResponse(request, DicomStatus.UnrecognizedOperation);
  193. }
  194. public DicomNEventReportResponse OnNEventReportRequest(DicomNEventReportRequest request)
  195. {
  196. Logger.Log(LogLevel.Info, "receiving N-Event, not supported");
  197. return new DicomNEventReportResponse(request, DicomStatus.UnrecognizedOperation);
  198. }
  199. public DicomNGetResponse OnNGetRequest(DicomNGetRequest request)
  200. {
  201. Logger.Log(LogLevel.Info, "receiving N-Get, not supported");
  202. return new DicomNGetResponse(request, DicomStatus.UnrecognizedOperation);
  203. }
  204. public DicomNActionResponse OnNActionRequest(DicomNActionRequest request)
  205. {
  206. Logger.Log(LogLevel.Info, "receiving N-Action, not supported");
  207. return new DicomNActionResponse(request, DicomStatus.UnrecognizedOperation);
  208. }
  209. #endregion
  210. }
  211. }