PageRenderTime 50ms CodeModel.GetById 20ms RepoModel.GetById 0ms app.codeStats 0ms

/DICOM/Network/DicomService.cs

https://github.com/petnet/fo-dicom
C# | 763 lines | 632 code | 124 blank | 7 comment | 101 complexity | a1f325e3ee92ef9fe758b0cc80daa48f MD5 | raw file
Possible License(s): BSD-3-Clause
  1. using System;
  2. using System.Collections.Generic;
  3. using System.IO;
  4. using System.Linq;
  5. using System.ServiceModel.Channels;
  6. using System.Text;
  7. using System.Threading;
  8. using System.Threading.Tasks;
  9. using NLog;
  10. using Dicom.Log;
  11. using Dicom.Imaging.Codec;
  12. using Dicom.IO;
  13. using Dicom.IO.Reader;
  14. using Dicom.IO.Writer;
  15. namespace Dicom.Network {
  16. public abstract class DicomService {
  17. private Stream _network;
  18. private object _lock;
  19. private volatile bool _writing;
  20. private volatile bool _sending;
  21. private Queue<PDU> _pduQueue;
  22. private Queue<DicomMessage> _msgQueue;
  23. private List<DicomRequest> _pending;
  24. private DicomMessage _dimse;
  25. private Stream _dimseStream;
  26. private int _readLength;
  27. private bool _isConnected;
  28. protected DicomService(Stream stream) {
  29. _network = stream;
  30. _lock = new object();
  31. _pduQueue = new Queue<PDU>();
  32. _msgQueue = new Queue<DicomMessage>();
  33. _pending = new List<DicomRequest>();
  34. _isConnected = true;
  35. Logger = LogManager.GetLogger("Dicom.Network");
  36. BeginReadPDUHeader();
  37. }
  38. protected Logger Logger {
  39. get;
  40. private set;
  41. }
  42. private string LogID {
  43. get;
  44. set;
  45. }
  46. public DicomAssociation Association {
  47. get;
  48. internal set;
  49. }
  50. public bool IsConnected {
  51. get {
  52. return _isConnected;
  53. }
  54. }
  55. private void BeginReadPDUHeader() {
  56. try {
  57. _readLength = 6;
  58. byte[] buffer = new byte[6];
  59. _network.BeginRead(buffer, 0, 6, EndReadPDUHeader, buffer);
  60. } catch (ObjectDisposedException) {
  61. // silently ignore
  62. _network.Close();
  63. _isConnected = false;
  64. }
  65. }
  66. private void EndReadPDUHeader(IAsyncResult result) {
  67. try {
  68. byte[] buffer = (byte[])result.AsyncState;
  69. int count = _network.EndRead(result);
  70. if (count == 0) {
  71. // disconnected
  72. _network.Close();
  73. _isConnected = false;
  74. return;
  75. }
  76. _readLength -= count;
  77. if (_readLength > 0) {
  78. _network.BeginRead(buffer, 6 - _readLength, _readLength, EndReadPDUHeader, buffer);
  79. return;
  80. }
  81. int length = BitConverter.ToInt32(buffer, 2);
  82. length = Endian.Swap(length);
  83. _readLength = length;
  84. Array.Resize(ref buffer, length + 6);
  85. _network.BeginRead(buffer, 6, length, EndReadPDU, buffer);
  86. } catch (ObjectDisposedException) {
  87. // silently ignore
  88. _network.Close();
  89. _isConnected = false;
  90. } catch (IOException) {
  91. // object disposed
  92. _network.Close();
  93. _isConnected = false;
  94. } catch (Exception e) {
  95. Logger.Log(LogLevel.Error, "Exception processing PDU header: {0}", e.ToString());
  96. }
  97. }
  98. private void EndReadPDU(IAsyncResult result) {
  99. try {
  100. byte[] buffer = (byte[])result.AsyncState;
  101. int count = _network.EndRead(result);
  102. if (count == 0) {
  103. // disconnected
  104. _network.Close();
  105. _isConnected = false;
  106. return;
  107. }
  108. _readLength -= count;
  109. if (_readLength > 0) {
  110. _network.BeginRead(buffer, buffer.Length - _readLength, _readLength, EndReadPDU, buffer);
  111. return;
  112. }
  113. var raw = new RawPDU(buffer);
  114. switch (raw.Type) {
  115. case 0x01: {
  116. Association = new DicomAssociation();
  117. var pdu = new AAssociateRQ(Association);
  118. pdu.Read(raw);
  119. LogID = Association.CallingAE;
  120. Logger.Log(LogLevel.Info, "{0} <- Association request:\n{1}", LogID, Association.ToString());
  121. if (this is IDicomServiceProvider)
  122. (this as IDicomServiceProvider).OnReceiveAssociationRequest(Association);
  123. break;
  124. }
  125. case 0x02: {
  126. var pdu = new AAssociateAC(Association);
  127. pdu.Read(raw);
  128. LogID = Association.CalledAE;
  129. Logger.Log(LogLevel.Info, "{0} <- Association accept:\n{1}", LogID, Association.ToString());
  130. if (this is IDicomServiceUser)
  131. (this as IDicomServiceUser).OnReceiveAssociationAccept(Association);
  132. break;
  133. }
  134. case 0x03: {
  135. var pdu = new AAssociateRJ();
  136. pdu.Read(raw);
  137. Logger.Log(LogLevel.Info, "{0} <- Association reject [result: {1}; source: {2}; reason: {3}]", LogID, pdu.Result, pdu.Source, pdu.Reason);
  138. if (this is IDicomServiceUser)
  139. (this as IDicomServiceUser).OnReceiveAssociationReject(pdu.Result, pdu.Source, pdu.Reason);
  140. break;
  141. }
  142. case 0x04: {
  143. var pdu = new PDataTF();
  144. pdu.Read(raw);
  145. ProcessPDataTF(pdu);
  146. break;
  147. }
  148. case 0x05: {
  149. var pdu = new AReleaseRQ();
  150. pdu.Read(raw);
  151. Logger.Log(LogLevel.Info, "{0} <- Association release request", LogID);
  152. if (this is IDicomServiceProvider)
  153. (this as IDicomServiceProvider).OnReceiveAssociationReleaseRequest();
  154. break;
  155. }
  156. case 0x06: {
  157. var pdu = new AReleaseRP();
  158. pdu.Read(raw);
  159. Logger.Log(LogLevel.Info, "{0} <- Association release response", LogID);
  160. if (this is IDicomServiceUser)
  161. (this as IDicomServiceUser).OnReceiveAssociationReleaseResponse();
  162. _network.Close();
  163. _isConnected = false;
  164. break;
  165. }
  166. case 0x07: {
  167. var pdu = new AAbort();
  168. pdu.Read(raw);
  169. Logger.Log(LogLevel.Info, "{0} <- Abort: {1} - {2}", LogID, pdu.Source, pdu.Reason);
  170. if (this is IDicomServiceProvider)
  171. (this as IDicomServiceProvider).OnReceiveAbort(pdu.Source, pdu.Reason);
  172. else if (this is IDicomServiceUser)
  173. (this as IDicomServiceUser).OnReceiveAbort(pdu.Source, pdu.Reason);
  174. _network.Close();
  175. _isConnected = false;
  176. break;
  177. }
  178. case 0xFF: {
  179. break;
  180. }
  181. default:
  182. throw new DicomNetworkException("Unknown PDU type");
  183. }
  184. BeginReadPDUHeader();
  185. } catch (Exception e) {
  186. Logger.Log(LogLevel.Error, "Exception processing PDU: {0}", e.ToString());
  187. _network.Close();
  188. _isConnected = false;
  189. }
  190. }
  191. private void ProcessPDataTF(PDataTF pdu) {
  192. try {
  193. foreach (var pdv in pdu.PDVs) {
  194. if (_dimse == null) {
  195. // create stream for receiving command
  196. if (_dimseStream == null)
  197. _dimseStream = new MemoryStream();
  198. } else {
  199. // create stream for receiving dataset
  200. if (_dimseStream == null) {
  201. if (_dimse.Type == DicomCommandField.CStoreRequest) {
  202. var pc = Association.PresentationContexts.FirstOrDefault(x => x.ID == pdv.PCID);
  203. var file = new DicomFile();
  204. file.FileMetaInfo.MediaStorageSOPClassUID = pc.AbstractSyntax;
  205. file.FileMetaInfo.MediaStorageSOPInstanceUID = _dimse.Command.Get<DicomUID>(DicomTag.AffectedSOPInstanceUID);
  206. file.FileMetaInfo.TransferSyntax = pc.AcceptedTransferSyntax;
  207. file.FileMetaInfo.ImplementationClassUID = Association.RemoteImplemetationClassUID;
  208. file.FileMetaInfo.ImplementationVersionName = Association.RemoteImplementationVersion;
  209. file.FileMetaInfo.SourceApplicationEntityTitle = Association.CallingAE;
  210. string fileName;
  211. if (this is IDicomCStoreProvider)
  212. fileName = (this as IDicomCStoreProvider).GetTempFileName(file.FileMetaInfo.MediaStorageSOPInstanceUID);
  213. else
  214. throw new DicomNetworkException("C-Store SCP not implemented");
  215. file.Save(fileName);
  216. _dimseStream = File.OpenWrite(fileName);
  217. _dimseStream.Seek(0, SeekOrigin.End);
  218. } else {
  219. _dimseStream = new MemoryStream();
  220. }
  221. }
  222. }
  223. _dimseStream.Write(pdv.Value, 0, pdv.Value.Length);
  224. if (pdv.IsLastFragment) {
  225. if (pdv.IsCommand) {
  226. _dimseStream.Seek(0, SeekOrigin.Begin);
  227. var command = new DicomDataset();
  228. var reader = new DicomReader();
  229. reader.IsExplicitVR = false;
  230. reader.Read(new StreamByteSource(_dimseStream), new DicomDatasetReaderObserver(command));
  231. _dimseStream = null;
  232. var type = command.Get<DicomCommandField>(DicomTag.CommandField);
  233. switch (type) {
  234. case DicomCommandField.CStoreRequest:
  235. _dimse = new DicomCStoreRequest(command);
  236. break;
  237. case DicomCommandField.CStoreResponse:
  238. _dimse = new DicomCStoreResponse(command);
  239. break;
  240. case DicomCommandField.CFindRequest:
  241. _dimse = new DicomCFindRequest(command);
  242. break;
  243. case DicomCommandField.CFindResponse:
  244. _dimse = new DicomCFindResponse(command);
  245. break;
  246. case DicomCommandField.CMoveRequest:
  247. _dimse = new DicomCMoveRequest(command);
  248. break;
  249. case DicomCommandField.CMoveResponse:
  250. _dimse = new DicomCMoveResponse(command);
  251. break;
  252. case DicomCommandField.CEchoRequest:
  253. _dimse = new DicomCEchoRequest(command);
  254. break;
  255. case DicomCommandField.CEchoResponse:
  256. _dimse = new DicomCEchoResponse(command);
  257. break;
  258. default:
  259. _dimse = new DicomMessage(command);
  260. break;
  261. }
  262. if (!_dimse.HasDataset) {
  263. ThreadPool.QueueUserWorkItem(PerformDimseCallback, _dimse);
  264. _dimse = null;
  265. return;
  266. }
  267. } else {
  268. if (_dimse.Type != DicomCommandField.CStoreRequest) {
  269. _dimseStream.Seek(0, SeekOrigin.Begin);
  270. _dimse.Dataset = new DicomDataset();
  271. _dimse.Dataset.InternalTransferSyntax = _dimse.Command.InternalTransferSyntax;
  272. var source = new StreamByteSource(_dimseStream);
  273. source.Endian = _dimse.Command.InternalTransferSyntax.Endian;
  274. var reader = new DicomReader();
  275. reader.IsExplicitVR = _dimse.Command.InternalTransferSyntax.IsExplicitVR;
  276. reader.Read(source, new DicomDatasetReaderObserver(_dimse.Dataset));
  277. _dimseStream = null;
  278. } else {
  279. var fileName = (_dimseStream as FileStream).Name;
  280. _dimseStream.Close();
  281. _dimseStream = null;
  282. var request = _dimse as DicomCStoreRequest;
  283. request.File = DicomFile.Open(fileName);
  284. request.File.File.IsTempFile = true;
  285. request.Dataset = request.File.Dataset;
  286. }
  287. ThreadPool.QueueUserWorkItem(PerformDimseCallback, _dimse);
  288. _dimse = null;
  289. }
  290. }
  291. }
  292. } catch (Exception e) {
  293. SendAbort(DicomAbortSource.ServiceUser, DicomAbortReason.NotSpecified);
  294. Logger.Log(LogLevel.Error, e.ToString());
  295. } finally {
  296. SendNextMessage();
  297. }
  298. }
  299. private void PerformDimseCallback(object state) {
  300. var dimse = state as DicomMessage;
  301. try {
  302. Logger.Log(LogLevel.Info, "{0} <- {1}", LogID, dimse);
  303. if (!DicomMessage.IsRequest(dimse.Type))
  304. return;
  305. if (dimse.Type == DicomCommandField.CStoreRequest) {
  306. if (this is IDicomCStoreProvider) {
  307. var response = (this as IDicomCStoreProvider).OnCStoreRequest(dimse as DicomCStoreRequest);
  308. SendResponse(response);
  309. return;
  310. } else
  311. throw new DicomNetworkException("C-Store SCP not implemented");
  312. }
  313. if (dimse.Type == DicomCommandField.CFindRequest) {
  314. if (this is IDicomCFindProvider) {
  315. var responses = (this as IDicomCFindProvider).OnCFindRequest(dimse as DicomCFindRequest);
  316. foreach (var response in responses)
  317. SendResponse(response);
  318. return;
  319. } else
  320. throw new DicomNetworkException("C-Find SCP not implemented");
  321. }
  322. if (dimse.Type == DicomCommandField.CMoveRequest) {
  323. if (this is IDicomCMoveProvider) {
  324. var responses = (this as IDicomCMoveProvider).OnCMoveRequest(dimse as DicomCMoveRequest);
  325. foreach (var response in responses)
  326. SendResponse(response);
  327. return;
  328. } else
  329. throw new DicomNetworkException("C-Move SCP not implemented");
  330. }
  331. if (dimse.Type == DicomCommandField.CEchoRequest) {
  332. if (this is IDicomCEchoProvider) {
  333. var response = (this as IDicomCEchoProvider).OnCEchoRequest(dimse as DicomCEchoRequest);
  334. SendResponse(response);
  335. return;
  336. } else
  337. throw new DicomNetworkException("C-Echo SCP not implemented");
  338. }
  339. throw new DicomNetworkException("Operation not implemented");
  340. } finally {
  341. if (dimse is DicomResponse) {
  342. var rsp = dimse as DicomResponse;
  343. lock (_lock) {
  344. var req = _pending.FirstOrDefault(x => x.MessageID == rsp.RequestMessageID);
  345. if (req != null) {
  346. (req as DicomRequest).PostResponse(this, rsp);
  347. if (rsp.Status.State != DicomState.Pending)
  348. _pending.Remove(req);
  349. }
  350. }
  351. }
  352. SendNextMessage();
  353. }
  354. }
  355. protected void SendPDU(PDU pdu) {
  356. lock (_lock)
  357. _pduQueue.Enqueue(pdu);
  358. SendNextPDU();
  359. }
  360. private void SendNextPDU() {
  361. PDU pdu;
  362. lock (_lock) {
  363. if (_writing)
  364. return;
  365. if (_pduQueue.Count == 0)
  366. return;
  367. _writing = true;
  368. pdu = _pduQueue.Dequeue();
  369. }
  370. MemoryStream ms = new MemoryStream();
  371. pdu.Write().WritePDU(ms);
  372. byte[] buffer = ms.ToArray();
  373. _network.BeginWrite(buffer, 0, (int)ms.Length, OnEndSendPDU, buffer);
  374. }
  375. private void OnEndSendPDU(IAsyncResult ar) {
  376. byte[] buffer = (byte[])ar.AsyncState;
  377. try {
  378. _network.EndWrite(ar);
  379. } catch {
  380. } finally {
  381. lock (_lock)
  382. _writing = false;
  383. SendNextPDU();
  384. }
  385. }
  386. private void SendMessage(DicomMessage message) {
  387. lock (_lock)
  388. _msgQueue.Enqueue(message);
  389. SendNextMessage();
  390. }
  391. private class Dimse {
  392. public DicomMessage Message;
  393. public PDataTFStream Stream;
  394. public DicomDatasetWalker Walker;
  395. public DicomPresentationContext PresentationContext;
  396. }
  397. private void SendNextMessage() {
  398. DicomMessage msg;
  399. lock (_lock) {
  400. if (_msgQueue.Count == 0) {
  401. if (_pending.Count == 0)
  402. OnSendQueueEmpty();
  403. return;
  404. }
  405. if (_sending)
  406. return;
  407. if (Association.MaxAsyncOpsInvoked > 0 && _pending.Count >= Association.MaxAsyncOpsInvoked)
  408. return;
  409. _sending = true;
  410. msg = _msgQueue.Dequeue();
  411. }
  412. Logger.Log(LogLevel.Info, "{0} -> {1}", LogID, msg);
  413. if (msg is DicomRequest)
  414. _pending.Add(msg as DicomRequest);
  415. DicomPresentationContext pc = null;
  416. if (msg is DicomCStoreRequest) {
  417. pc = Association.PresentationContexts.FirstOrDefault(x => x.Result == DicomPresentationContextResult.Accept && x.AbstractSyntax == msg.AffectedSOPClassUID && x.AcceptedTransferSyntax == (msg as DicomCStoreRequest).TransferSyntax);
  418. if (pc == null)
  419. pc = Association.PresentationContexts.FirstOrDefault(x => x.Result == DicomPresentationContextResult.Accept && x.AbstractSyntax == msg.AffectedSOPClassUID);
  420. } else {
  421. pc = Association.PresentationContexts.FirstOrDefault(x => x.Result == DicomPresentationContextResult.Accept && x.AbstractSyntax == msg.AffectedSOPClassUID);
  422. }
  423. if (pc == null)
  424. throw new DicomNetworkException("No accepted presentation context found for abstract syntax: {0}", msg.AffectedSOPClassUID);
  425. var dimse = new Dimse();
  426. dimse.Message = msg;
  427. dimse.PresentationContext = pc;
  428. dimse.Stream = new PDataTFStream(this, pc.ID, (int)Association.MaximumPDULength);
  429. var writer = new DicomWriter(DicomTransferSyntax.ImplicitVRLittleEndian, DicomWriteOptions.Default, new StreamByteTarget(dimse.Stream));
  430. dimse.Walker = new DicomDatasetWalker(msg.Command);
  431. dimse.Walker.BeginWalk(writer, OnEndSendCommand, dimse);
  432. }
  433. private void OnEndSendCommand(IAsyncResult result) {
  434. var dimse = result.AsyncState as Dimse;
  435. try {
  436. dimse.Walker.EndWalk(result);
  437. if (!dimse.Message.HasDataset) {
  438. dimse.Stream.Flush(true);
  439. dimse.Stream.Close();
  440. return;
  441. }
  442. dimse.Stream.IsCommand = false;
  443. var dataset = dimse.Message.Dataset;
  444. if (dataset.InternalTransferSyntax != dimse.PresentationContext.AcceptedTransferSyntax)
  445. dataset = dataset.ChangeTransferSyntax(dimse.PresentationContext.AcceptedTransferSyntax);
  446. var writer = new DicomWriter(dimse.PresentationContext.AcceptedTransferSyntax, DicomWriteOptions.Default, new StreamByteTarget(dimse.Stream));
  447. dimse.Walker = new DicomDatasetWalker(dataset);
  448. dimse.Walker.BeginWalk(writer, OnEndSendMessage, dimse);
  449. } catch {
  450. } finally {
  451. if (!dimse.Message.HasDataset) {
  452. lock (_lock)
  453. _sending = false;
  454. SendNextMessage();
  455. }
  456. }
  457. }
  458. private void OnEndSendMessage(IAsyncResult result) {
  459. var dimse = result.AsyncState as Dimse;
  460. try {
  461. dimse.Walker.EndWalk(result);
  462. } catch {
  463. } finally {
  464. dimse.Stream.Flush(true);
  465. dimse.Stream.Close();
  466. lock (_lock)
  467. _sending = false;
  468. SendNextMessage();
  469. }
  470. }
  471. public void SendRequest(DicomRequest request) {
  472. SendMessage(request);
  473. }
  474. protected void SendResponse(DicomResponse response) {
  475. SendMessage(response);
  476. }
  477. private class PDataTFStream : Stream {
  478. #region Private Members
  479. private DicomService _service;
  480. private bool _command;
  481. private int _max;
  482. private byte _pcid;
  483. private PDataTF _pdu;
  484. private byte[] _bytes;
  485. private int _sent;
  486. private MemoryStream _buffer;
  487. #endregion
  488. #region Public Constructors
  489. public PDataTFStream(DicomService service, byte pcid, int max) {
  490. _service = service;
  491. _command = true;
  492. _pcid = pcid;
  493. _max = (max == 0) ? MaxPduSizeLimit : Math.Min(max, MaxPduSizeLimit);
  494. _pdu = new PDataTF();
  495. _buffer = new MemoryStream((int)_max * 2);
  496. }
  497. #endregion
  498. #region Public Properties
  499. public static int MaxPduSizeLimit = 16384;
  500. public bool IsCommand {
  501. get { return _command; }
  502. set {
  503. CreatePDV();
  504. _command = value;
  505. WritePDU(true);
  506. }
  507. }
  508. public int BytesSent {
  509. get { return _sent; }
  510. }
  511. #endregion
  512. #region Public Members
  513. public void Flush(bool last) {
  514. WritePDU(last);
  515. }
  516. #endregion
  517. #region Private Members
  518. private int CurrentPduSize() {
  519. return 6 + (int)_pdu.GetLengthOfPDVs();
  520. }
  521. private bool CreatePDV() {
  522. int len = Math.Min(GetBufferLength(), _max - CurrentPduSize() - 6);
  523. if (_bytes == null || _bytes.Length != len || _pdu.PDVs.Count > 0) {
  524. _bytes = new byte[len];
  525. }
  526. _sent = _buffer.Read(_bytes, 0, len);
  527. PDV pdv = new PDV(_pcid, _bytes, _command, false);
  528. _pdu.PDVs.Add(pdv);
  529. return pdv.IsLastFragment;
  530. }
  531. private void WritePDU(bool last) {
  532. if (_pdu.PDVs.Count == 0 || ((CurrentPduSize() + 6) < _max && GetBufferLength() > 0)) {
  533. CreatePDV();
  534. }
  535. if (_pdu.PDVs.Count > 0) {
  536. if (last)
  537. _pdu.PDVs[_pdu.PDVs.Count - 1].IsLastFragment = true;
  538. _service.SendPDU(_pdu);
  539. _pdu = new PDataTF();
  540. }
  541. }
  542. private void AppendBuffer(byte[] buffer, int offset, int count) {
  543. long pos = _buffer.Position;
  544. _buffer.Seek(0, SeekOrigin.End);
  545. _buffer.Write(buffer, offset, count);
  546. _buffer.Position = pos;
  547. }
  548. private int GetBufferLength() {
  549. return (int)(_buffer.Length - _buffer.Position);
  550. }
  551. #endregion
  552. #region Stream Members
  553. public override bool CanRead {
  554. get { return false; }
  555. }
  556. public override bool CanSeek {
  557. get { return false; }
  558. }
  559. public override bool CanWrite {
  560. get { return true; }
  561. }
  562. public override void Flush() {
  563. }
  564. public override long Length {
  565. get { throw new NotImplementedException(); }
  566. }
  567. public override long Position {
  568. get {
  569. throw new NotImplementedException();
  570. }
  571. set {
  572. throw new NotImplementedException();
  573. }
  574. }
  575. public override int Read(byte[] buffer, int offset, int count) {
  576. throw new NotImplementedException();
  577. }
  578. public override long Seek(long offset, SeekOrigin origin) {
  579. throw new NotImplementedException();
  580. }
  581. public override void SetLength(long value) {
  582. throw new NotImplementedException();
  583. }
  584. public override void Write(byte[] buffer, int offset, int count) {
  585. AppendBuffer(buffer, offset, count);
  586. while ((CurrentPduSize() + 6 + GetBufferLength()) > _max) {
  587. WritePDU(false);
  588. }
  589. }
  590. public void Write(Stream stream) {
  591. int max = _max - 12;
  592. int length = (int)stream.Length;
  593. int position = (int)stream.Position;
  594. byte[] buffer = new byte[max];
  595. while (position < length) {
  596. int count = Math.Min(max, length - position);
  597. count = stream.Read(buffer, 0, count);
  598. AppendBuffer(buffer, 0, count);
  599. position += count;
  600. WritePDU(position == length);
  601. }
  602. }
  603. #endregion
  604. }
  605. #region Send Methods
  606. protected void SendAssociationRequest(DicomAssociation association) {
  607. LogID = association.CalledAE;
  608. Logger.Log(LogLevel.Info, "{0} -> Association request:\n{1}", LogID, association.ToString());
  609. Association = association;
  610. SendPDU(new AAssociateRQ(Association));
  611. }
  612. protected void SendAssociationAccept(DicomAssociation association) {
  613. Association = association;
  614. Logger.Log(LogLevel.Info, "{0} -> Association accept:\n{1}", LogID, association.ToString());
  615. SendPDU(new AAssociateAC(Association));
  616. }
  617. protected void SendAssociationReject(DicomRejectResult result, DicomRejectSource source, DicomRejectReason reason) {
  618. Logger.Log(LogLevel.Info, "{0} -> Association reject [result: {1}; source: {2}; reason: {3}]", LogID, result, source, reason);
  619. SendPDU(new AAssociateRJ(result, source, reason));
  620. }
  621. protected void SendAssociationReleaseRequest() {
  622. Logger.Log(LogLevel.Info, "{0} -> Association release request", LogID);
  623. SendPDU(new AReleaseRQ());
  624. }
  625. protected void SendAssociationReleaseResponse() {
  626. Logger.Log(LogLevel.Info, "{0} -> Association release response", LogID);
  627. SendPDU(new AReleaseRP());
  628. }
  629. protected void SendAbort(DicomAbortSource source, DicomAbortReason reason) {
  630. Logger.Log(LogLevel.Info, "{0} -> Abort [source: {1}; reason: {2}]", LogID, source, reason);
  631. SendPDU(new AAbort(source, reason));
  632. }
  633. #endregion
  634. #region Override Methods
  635. protected virtual void OnSendQueueEmpty() {
  636. }
  637. #endregion
  638. }
  639. }