comparison Core/DicomNetworking/DicomStoreUserConnection.cpp @ 3825:4570c57668a8

refactoring DicomUserConnection as Dicom[Control|Store]UserConnection
author Sebastien Jodogne <s.jodogne@gmail.com>
date Fri, 10 Apr 2020 16:04:54 +0200
parents
children e82bd07c384e
comparison
equal deleted inserted replaced
3817:37e20bbf25f5 3825:4570c57668a8
1 /**
2 * Orthanc - A Lightweight, RESTful DICOM Store
3 * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
4 * Department, University Hospital of Liege, Belgium
5 * Copyright (C) 2017-2020 Osimis S.A., Belgium
6 *
7 * This program is free software: you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License as
9 * published by the Free Software Foundation, either version 3 of the
10 * License, or (at your option) any later version.
11 *
12 * In addition, as a special exception, the copyright holders of this
13 * program give permission to link the code of its release with the
14 * OpenSSL project's "OpenSSL" library (or with modified versions of it
15 * that use the same license as the "OpenSSL" library), and distribute
16 * the linked executables. You must obey the GNU General Public License
17 * in all respects for all of the code used other than "OpenSSL". If you
18 * modify file(s) with this exception, you may extend this exception to
19 * your version of the file(s), but you are not obligated to do so. If
20 * you do not wish to do so, delete this exception statement from your
21 * version. If you delete this exception statement from all source files
22 * in the program, then also delete it here.
23 *
24 * This program is distributed in the hope that it will be useful, but
25 * WITHOUT ANY WARRANTY; without even the implied warranty of
26 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
27 * General Public License for more details.
28 *
29 * You should have received a copy of the GNU General Public License
30 * along with this program. If not, see <http://www.gnu.org/licenses/>.
31 **/
32
33
34 #include "../PrecompiledHeaders.h"
35 #include "DicomStoreUserConnection.h"
36
37 #include "../Logging.h"
38 #include "../OrthancException.h"
39
40 namespace Orthanc
41 {
42 bool DicomStoreUserConnection::ProposeStorageClass(const std::string& sopClassUid,
43 const std::set<DicomTransferSyntax>& syntaxes)
44 {
45 size_t requiredCount = syntaxes.size();
46 if (proposeUncompressedSyntaxes_)
47 {
48 requiredCount += 1;
49 }
50
51 if (association_.GetRemainingPropositions() <= requiredCount)
52 {
53 return false; // Not enough room
54 }
55
56 for (std::set<DicomTransferSyntax>::const_iterator
57 it = syntaxes.begin(); it != syntaxes.end(); ++it)
58 {
59 association_.ProposePresentationContext(sopClassUid, *it);
60 }
61
62 if (proposeUncompressedSyntaxes_)
63 {
64 std::set<DicomTransferSyntax> uncompressed;
65
66 if (syntaxes.find(DicomTransferSyntax_LittleEndianImplicit) == syntaxes.end())
67 {
68 uncompressed.insert(DicomTransferSyntax_LittleEndianImplicit);
69 }
70
71 if (syntaxes.find(DicomTransferSyntax_LittleEndianExplicit) == syntaxes.end())
72 {
73 uncompressed.insert(DicomTransferSyntax_LittleEndianExplicit);
74 }
75
76 if (proposeRetiredBigEndian_ &&
77 syntaxes.find(DicomTransferSyntax_BigEndianExplicit) == syntaxes.end())
78 {
79 uncompressed.insert(DicomTransferSyntax_BigEndianExplicit);
80 }
81
82 if (!uncompressed.empty())
83 {
84 association_.ProposePresentationContext(sopClassUid, uncompressed);
85 }
86 }
87
88 return true;
89 }
90
91
92 bool DicomStoreUserConnection::LookupPresentationContext(
93 uint8_t& presentationContextId,
94 const std::string& sopClassUid,
95 DicomTransferSyntax transferSyntax)
96 {
97 typedef std::map<DicomTransferSyntax, uint8_t> PresentationContexts;
98
99 PresentationContexts pc;
100 if (association_.IsOpen() &&
101 association_.LookupAcceptedPresentationContext(pc, sopClassUid))
102 {
103 PresentationContexts::const_iterator found = pc.find(transferSyntax);
104 if (found != pc.end())
105 {
106 presentationContextId = found->second;
107 return true;
108 }
109 }
110
111 return false;
112 }
113
114
115 DicomStoreUserConnection::DicomStoreUserConnection(
116 const DicomAssociationParameters& params) :
117 parameters_(params),
118 proposeCommonClasses_(true),
119 proposeUncompressedSyntaxes_(true),
120 proposeRetiredBigEndian_(false)
121 {
122 }
123
124
125 void DicomStoreUserConnection::PrepareStorageClass(const std::string& sopClassUid,
126 DicomTransferSyntax syntax)
127 {
128 StorageClasses::iterator found = storageClasses_.find(sopClassUid);
129
130 if (found == storageClasses_.end())
131 {
132 std::set<DicomTransferSyntax> ts;
133 ts.insert(syntax);
134 storageClasses_[sopClassUid] = ts;
135 }
136 else
137 {
138 found->second.insert(syntax);
139 }
140 }
141
142
143 bool DicomStoreUserConnection::NegotiatePresentationContext(
144 uint8_t& presentationContextId,
145 const std::string& sopClassUid,
146 DicomTransferSyntax transferSyntax)
147 {
148 /**
149 * Step 1: Check whether this presentation context is already
150 * available in the previously negociated assocation.
151 **/
152
153 if (LookupPresentationContext(presentationContextId, sopClassUid, transferSyntax))
154 {
155 return true;
156 }
157
158 // The association must be re-negotiated
159 LOG(INFO) << "Re-negociating DICOM association with "
160 << parameters_.GetRemoteApplicationEntityTitle();
161 association_.ClearPresentationContexts();
162 PrepareStorageClass(sopClassUid, transferSyntax);
163
164
165 /**
166 * Step 2: Propose at least the mandatory SOP class.
167 **/
168
169 {
170 StorageClasses::const_iterator mandatory = storageClasses_.find(sopClassUid);
171
172 if (mandatory == storageClasses_.end() ||
173 mandatory->second.find(transferSyntax) == mandatory->second.end())
174 {
175 throw OrthancException(ErrorCode_InternalError);
176 }
177
178 if (!ProposeStorageClass(sopClassUid, mandatory->second))
179 {
180 // Should never happen in real life: There are no more than
181 // 128 transfer syntaxes in DICOM!
182 throw OrthancException(ErrorCode_InternalError,
183 "Too many transfer syntaxes for SOP class UID: " + sopClassUid);
184 }
185 }
186
187
188 /**
189 * Step 3: Propose all the previously spotted SOP classes, as
190 * registered through the "PrepareStorageClass()" method.
191 **/
192
193 for (StorageClasses::const_iterator it = storageClasses_.begin();
194 it != storageClasses_.end(); ++it)
195 {
196 if (it->first != sopClassUid)
197 {
198 ProposeStorageClass(it->first, it->second);
199 }
200 }
201
202
203 /**
204 * Step 4: As long as there is room left in the proposed
205 * presentation contexts, propose the uncompressed transfer syntaxes
206 * for the most common SOP classes, as can be found in the
207 * "dcmShortSCUStorageSOPClassUIDs" array from DCMTK. The
208 * preferred transfer syntax is "LittleEndianImplicit".
209 **/
210
211 if (proposeCommonClasses_)
212 {
213 std::set<DicomTransferSyntax> ts;
214 ts.insert(DicomTransferSyntax_LittleEndianImplicit);
215
216 for (int i = 0; i < numberOfDcmShortSCUStorageSOPClassUIDs; i++)
217 {
218 std::string c(dcmShortSCUStorageSOPClassUIDs[i]);
219
220 if (c != sopClassUid &&
221 storageClasses_.find(c) == storageClasses_.end())
222 {
223 ProposeStorageClass(c, ts);
224 }
225 }
226 }
227
228
229 /**
230 * Step 5: Open the association, and check whether the pair (SOP
231 * class UID, transfer syntax) was accepted by the remote host.
232 **/
233
234 association_.Open(parameters_);
235 return LookupPresentationContext(presentationContextId, sopClassUid, transferSyntax);
236 }
237 }