Mercurial > hg > orthanc
comparison OrthancServer/OrthancGetRequestHandler.cpp @ 3955:66879215cbf3 c-get
C-GET: add timeout, fix uninitalized priority, support multiple resources
author | Sebastien Jodogne <s.jodogne@gmail.com> |
---|---|
date | Wed, 20 May 2020 16:38:33 +0200 |
parents | 67b457283499 |
children | 76a24be12912 |
comparison
equal
deleted
inserted
replaced
3954:67b457283499 | 3955:66879215cbf3 |
---|---|
53 { | 53 { |
54 namespace | 54 namespace |
55 { | 55 { |
56 // Anonymous namespace to avoid clashes between compilation modules | 56 // Anonymous namespace to avoid clashes between compilation modules |
57 | 57 |
58 static void getSubOpProgressCallback(void * /* callbackData */, | 58 static void GetSubOpProgressCallback( |
59 T_DIMSE_StoreProgress *progress, | 59 void * /* callbackData == pointer to the "OrthancGetRequestHandler" object */, |
60 T_DIMSE_C_StoreRQ * /*req*/) | 60 T_DIMSE_StoreProgress *progress, |
61 T_DIMSE_C_StoreRQ * /*req*/) | |
61 { | 62 { |
62 // SBL - no logging to be done here. | 63 // SBL - no logging to be done here. |
63 } | 64 } |
64 } | 65 } |
65 | 66 |
78 if (dicom.size() <= 0) | 79 if (dicom.size() <= 0) |
79 { | 80 { |
80 return Status_Failure; | 81 return Status_Failure; |
81 } | 82 } |
82 | 83 |
83 std::unique_ptr<DcmFileFormat> parsed( | 84 ParsedDicomFile parsed(dicom); |
84 FromDcmtkBridge::LoadFromMemoryBuffer(dicom.c_str(), dicom.size())); | 85 |
85 | 86 if (parsed.GetDcmtkObject().getDataset() == NULL) |
86 // Determine the storage SOP class UID for this instance | 87 { |
87 DIC_UI sopClass; | 88 throw OrthancException(ErrorCode_InternalError); |
88 DIC_UI sopInstance; | 89 } |
89 | 90 |
90 { | 91 DcmDataset& dataset = *parsed.GetDcmtkObject().getDataset(); |
91 bool ok; | 92 |
92 | 93 OFString a, b; |
93 #if DCMTK_VERSION_NUMBER >= 364 | 94 if (!dataset.findAndGetOFString(DCM_SOPClassUID, a).good() || |
94 ok = DU_findSOPClassAndInstanceInDataSet(static_cast<DcmItem *> (parsed->getDataset()), | 95 !dataset.findAndGetOFString(DCM_SOPInstanceUID, b).good()) |
95 sopClass, sizeof(sopClass), | 96 { |
96 sopInstance, sizeof(sopInstance)); | 97 throw OrthancException(ErrorCode_NoSopClassOrInstance, |
97 #else | 98 "Unable to determine the SOP class/instance for C-STORE with AET " + |
98 ok = DU_findSOPClassAndInstanceInDataSet(parsed->getDataset(), sopClass, sopInstance); | 99 originatorAet_); |
99 #endif | 100 } |
100 | 101 |
101 if (!ok) | 102 std::string sopClassUid(a.c_str()); |
102 { | 103 std::string sopInstanceUid(b.c_str()); |
103 throw OrthancException(ErrorCode_NoSopClassOrInstance, | 104 |
104 "Unable to determine the SOP class/instance for C-STORE with AET " + | 105 OFCondition cond = PerformGetSubOp(assoc, sopClassUid, sopInstanceUid, dataset); |
105 originatorAet_); | |
106 } | |
107 } | |
108 | |
109 OFCondition cond = PerformGetSubOp(assoc, sopClass, sopInstance, parsed->getDataset()); | |
110 | 106 |
111 if (getCancelled_) | 107 if (getCancelled_) |
112 { | 108 { |
113 LOG(INFO) << "Get SCP: Received C-Cancel RQ"; | 109 LOG(INFO) << "Get SCP: Received C-Cancel RQ"; |
114 } | 110 } |
120 | 116 |
121 return Status_Success; | 117 return Status_Success; |
122 } | 118 } |
123 | 119 |
124 | 120 |
125 void OrthancGetRequestHandler::AddFailedUIDInstance(const char *sopInstance) | 121 void OrthancGetRequestHandler::AddFailedUIDInstance(const std::string& sopInstance) |
126 { | 122 { |
127 if (failedUIDs_.empty()) | 123 if (failedUIDs_.empty()) |
128 { | 124 { |
129 failedUIDs_ = sopInstance; | 125 failedUIDs_ = sopInstance; |
130 } | 126 } |
131 else | 127 else |
132 { | 128 { |
133 failedUIDs_ += "\\" + std::string(sopInstance); | 129 failedUIDs_ += "\\" + sopInstance; |
134 } | 130 } |
135 } | 131 } |
136 | 132 |
137 | 133 |
138 OFCondition OrthancGetRequestHandler::PerformGetSubOp(T_ASC_Association* assoc, | 134 OFCondition OrthancGetRequestHandler::PerformGetSubOp(T_ASC_Association* assoc, |
139 DIC_UI sopClass, | 135 const std::string& sopClassUid, |
140 DIC_UI sopInstance, | 136 const std::string& sopInstanceUid, |
141 DcmDataset *dataset) | 137 DcmDataset& dataset) |
142 { | 138 { |
143 OFCondition cond = EC_Normal; | |
144 T_DIMSE_C_StoreRQ req; | |
145 T_DIMSE_C_StoreRSP rsp; | |
146 DIC_US msgId; | |
147 T_ASC_PresentationContextID presId; | 139 T_ASC_PresentationContextID presId; |
148 DcmDataset *stDetail = NULL; | |
149 | |
150 msgId = assoc->nextMsgID++; | |
151 | 140 |
152 // which presentation context should be used | 141 // which presentation context should be used |
153 presId = ASC_findAcceptedPresentationContextID(assoc, sopClass); | 142 presId = ASC_findAcceptedPresentationContextID(assoc, sopClassUid.c_str()); |
154 | 143 |
155 if (presId == 0) | 144 if (presId == 0) |
156 { | 145 { |
157 nFailed_++; | 146 nFailed_++; |
158 AddFailedUIDInstance(sopInstance); | 147 AddFailedUIDInstance(sopInstanceUid); |
159 LOG(ERROR) << "Get SCP: storeSCU: No presentation context for: (" | 148 LOG(ERROR) << "Get SCP: storeSCU: No presentation context for: (" |
160 << dcmSOPClassUIDToModality(sopClass, "OT") << ") " << sopClass; | 149 << dcmSOPClassUIDToModality(sopClassUid.c_str(), "OT") << ") " << sopClassUid; |
161 return DIMSE_NOVALIDPRESENTATIONCONTEXTID; | 150 return DIMSE_NOVALIDPRESENTATIONCONTEXTID; |
162 } | 151 } |
163 else | 152 else |
164 { | 153 { |
165 // make sure that we can send images in this presentation context | 154 // make sure that we can send images in this presentation context |
169 if ((pc.acceptedRole != ASC_SC_ROLE_SCP) && | 158 if ((pc.acceptedRole != ASC_SC_ROLE_SCP) && |
170 (pc.acceptedRole != ASC_SC_ROLE_SCUSCP)) | 159 (pc.acceptedRole != ASC_SC_ROLE_SCUSCP)) |
171 { | 160 { |
172 // the role is not appropriate | 161 // the role is not appropriate |
173 nFailed_++; | 162 nFailed_++; |
174 AddFailedUIDInstance(sopInstance); | 163 AddFailedUIDInstance(sopInstanceUid); |
175 LOG(ERROR) <<"Get SCP: storeSCU: [No presentation context with requestor SCP role for: (" | 164 LOG(ERROR) <<"Get SCP: storeSCU: [No presentation context with requestor SCP role for: (" |
176 << dcmSOPClassUIDToModality(sopClass, "OT") << ") " << sopClass; | 165 << dcmSOPClassUIDToModality(sopClassUid.c_str(), "OT") << ") " << sopClassUid; |
177 return DIMSE_NOVALIDPRESENTATIONCONTEXTID; | 166 return DIMSE_NOVALIDPRESENTATIONCONTEXTID; |
178 } | 167 } |
179 } | 168 } |
180 | 169 |
170 const DIC_US msgId = assoc->nextMsgID++; | |
171 | |
172 T_DIMSE_C_StoreRQ req; | |
173 memset(&req, 0, sizeof(req)); | |
181 req.MessageID = msgId; | 174 req.MessageID = msgId; |
182 strcpy(req.AffectedSOPClassUID, sopClass); | 175 strncpy(req.AffectedSOPClassUID, sopClassUid.c_str(), DIC_UI_LEN); |
183 strcpy(req.AffectedSOPInstanceUID, sopInstance); | 176 strncpy(req.AffectedSOPInstanceUID, sopInstanceUid.c_str(), DIC_UI_LEN); |
184 req.DataSetType = DIMSE_DATASET_PRESENT; | 177 req.DataSetType = DIMSE_DATASET_PRESENT; |
185 req.Priority = priority_; | 178 req.Priority = DIMSE_PRIORITY_MEDIUM; |
186 req.opts = 0; | 179 req.opts = 0; |
187 | 180 |
181 T_DIMSE_C_StoreRSP rsp; | |
182 memset(&rsp, 0, sizeof(rsp)); | |
183 | |
188 LOG(INFO) << "Store SCU RQ: MsgID " << msgId << ", (" | 184 LOG(INFO) << "Store SCU RQ: MsgID " << msgId << ", (" |
189 << dcmSOPClassUIDToModality(sopClass, "OT") << ")"; | 185 << dcmSOPClassUIDToModality(sopClassUid.c_str(), "OT") << ")"; |
190 | 186 |
191 T_DIMSE_DetectedCancelParameters cancelParameters; | 187 T_DIMSE_DetectedCancelParameters cancelParameters; |
192 | 188 memset(&cancelParameters, 0, sizeof(cancelParameters)); |
193 cond = DIMSE_storeUser(assoc, presId, &req, | 189 |
194 NULL, dataset, getSubOpProgressCallback, this, DIMSE_BLOCKING, 0, | 190 std::unique_ptr<DcmDataset> stDetail; |
195 &rsp, &stDetail, &cancelParameters); | 191 |
192 OFCondition cond; | |
193 | |
194 { | |
195 DcmDataset *stDetailTmp = NULL; | |
196 cond = DIMSE_storeUser(assoc, presId, &req, NULL /* imageFileName */, &dataset, | |
197 GetSubOpProgressCallback, this /* callbackData */, | |
198 (timeout_ > 0 ? DIMSE_NONBLOCKING : DIMSE_BLOCKING), timeout_, | |
199 &rsp, &stDetailTmp, &cancelParameters); | |
200 stDetail.reset(stDetailTmp); | |
201 } | |
196 | 202 |
197 if (cond.good()) | 203 if (cond.good()) |
198 { | 204 { |
199 if (cancelParameters.cancelEncountered) | 205 if (cancelParameters.cancelEncountered) |
200 { | 206 { |
223 << DU_cstoreStatusString(rsp.DimseStatus); | 229 << DU_cstoreStatusString(rsp.DimseStatus); |
224 } | 230 } |
225 else | 231 else |
226 { | 232 { |
227 nFailed_++; | 233 nFailed_++; |
228 AddFailedUIDInstance(sopInstance); | 234 AddFailedUIDInstance(sopInstanceUid); |
229 // print a status message | 235 // print a status message |
230 LOG(ERROR) << "Get SCP: Store Failed: Response Status: " | 236 LOG(ERROR) << "Get SCP: Store Failed: Response Status: " |
231 << DU_cstoreStatusString(rsp.DimseStatus); | 237 << DU_cstoreStatusString(rsp.DimseStatus); |
232 } | 238 } |
233 } | 239 } |
234 else | 240 else |
235 { | 241 { |
236 nFailed_++; | 242 nFailed_++; |
237 AddFailedUIDInstance(sopInstance); | 243 AddFailedUIDInstance(sopInstanceUid); |
238 OFString temp_str; | 244 OFString temp_str; |
239 LOG(ERROR) << "Get SCP: storeSCU: Store Request Failed: " << DimseCondition::dump(temp_str, cond); | 245 LOG(ERROR) << "Get SCP: storeSCU: Store Request Failed: " << DimseCondition::dump(temp_str, cond); |
240 } | 246 } |
241 | 247 |
242 if (stDetail) | 248 if (stDetail.get() != NULL) |
243 { | 249 { |
244 LOG(INFO) << " Status Detail:" << OFendl << DcmObject::PrintHelper(*stDetail); | 250 LOG(INFO) << " Status Detail:" << OFendl << DcmObject::PrintHelper(*stDetail); |
245 delete stDetail; | |
246 } | 251 } |
247 | 252 |
248 return cond; | 253 return cond; |
249 } | 254 } |
250 | 255 |
251 bool OrthancGetRequestHandler::LookupIdentifiers(std::vector<std::string>& publicIds, | 256 bool OrthancGetRequestHandler::LookupIdentifiers(std::list<std::string>& publicIds, |
252 ResourceType level, | 257 ResourceType level, |
253 const DicomMap& input) | 258 const DicomMap& input) const |
254 { | 259 { |
255 DicomTag tag(0, 0); // Dummy initialization | 260 DicomTag tag(0, 0); // Dummy initialization |
256 | 261 |
257 switch (level) | 262 switch (level) |
258 { | 263 { |
288 { | 293 { |
289 return false; | 294 return false; |
290 } | 295 } |
291 else | 296 else |
292 { | 297 { |
293 const std::string& content = value.GetContent(); | 298 std::vector<std::string> tokens; |
294 context_.GetIndex().LookupIdentifierExact(publicIds, level, tag, content); | 299 Toolbox::TokenizeString(tokens, value.GetContent(), '\\'); |
295 return true; | 300 |
296 } | 301 for (size_t i = 0; i < tokens.size(); i++) |
297 } | 302 { |
298 | 303 std::vector<std::string> tmp; |
299 | 304 context_.GetIndex().LookupIdentifierExact(tmp, level, tag, tokens[i]); |
300 OrthancGetRequestHandler::OrthancGetRequestHandler(ServerContext& context) : | 305 |
301 context_(context) | 306 if (tmp.empty()) |
302 { | 307 { |
303 position_ = 0; | 308 LOG(ERROR) << "C-GET: Cannot locate resource \"" << tokens[i] |
304 nRemaining_ = 0; | 309 << "\" at the " << EnumerationToString(level) << " level"; |
305 nCompleted_ = 0; | 310 return false; |
306 warningCount_ = 0; | 311 } |
307 nFailed_ = 0; | 312 else |
308 } | 313 { |
314 for (size_t i = 0; i < tmp.size(); i++) | |
315 { | |
316 publicIds.push_back(tmp[i]); | |
317 } | |
318 } | |
319 } | |
320 | |
321 return true; | |
322 } | |
323 } | |
324 | |
325 | |
326 OrthancGetRequestHandler::OrthancGetRequestHandler(ServerContext& context) : | |
327 context_(context) | |
328 { | |
329 position_ = 0; | |
330 nRemaining_ = 0; | |
331 nCompleted_ = 0; | |
332 warningCount_ = 0; | |
333 nFailed_ = 0; | |
334 timeout_ = 0; | |
335 } | |
309 | 336 |
310 | 337 |
311 bool OrthancGetRequestHandler::Handle(const DicomMap& input, | 338 bool OrthancGetRequestHandler::Handle(const DicomMap& input, |
312 const std::string& originatorIp, | 339 const std::string& originatorIp, |
313 const std::string& originatorAet, | 340 const std::string& originatorAet, |
314 const std::string& calledAet) | 341 const std::string& calledAet, |
342 uint32_t timeout) | |
315 { | 343 { |
316 MetricsRegistry::Timer timer(context_.GetMetricsRegistry(), "orthanc_get_scp_duration_ms"); | 344 MetricsRegistry::Timer timer(context_.GetMetricsRegistry(), "orthanc_get_scp_duration_ms"); |
317 | 345 |
318 LOG(WARNING) << "Get-SCU request received from AET \"" << originatorAet << "\""; | 346 LOG(WARNING) << "Get-SCU request received from AET \"" << originatorAet << "\""; |
319 | 347 |
342 | 370 |
343 /** | 371 /** |
344 * Lookup for the resource to be sent. | 372 * Lookup for the resource to be sent. |
345 **/ | 373 **/ |
346 | 374 |
347 std::vector<std::string> publicIds; | 375 std::list<std::string> publicIds; |
348 | 376 |
349 if (!LookupIdentifiers(publicIds, level, input)) | 377 if (!LookupIdentifiers(publicIds, level, input)) |
350 { | 378 { |
351 LOG(ERROR) << "Cannot determine what resources are requested by C-GET"; | 379 LOG(ERROR) << "Cannot determine what resources are requested by C-GET"; |
352 return false; | 380 return false; |
353 } | 381 } |
354 | 382 |
355 localAet_ = context_.GetDefaultLocalApplicationEntityTitle(); | 383 localAet_ = context_.GetDefaultLocalApplicationEntityTitle(); |
356 position_ = 0; | 384 position_ = 0; |
357 originatorAet_ = originatorAet; | 385 originatorAet_ = originatorAet; |
358 | 386 |
359 { | 387 { |
360 OrthancConfiguration::ReaderLock lock; | 388 OrthancConfiguration::ReaderLock lock; |
361 remote_ = lock.GetConfiguration().GetModalityUsingAet(originatorAet); | 389 remote_ = lock.GetConfiguration().GetModalityUsingAet(originatorAet); |
362 } | 390 } |
363 | 391 |
364 for (size_t i = 0; i < publicIds.size(); i++) | 392 for (std::list<std::string>::const_iterator |
365 { | 393 resource = publicIds.begin(); resource != publicIds.end(); ++resource) |
366 LOG(INFO) << "C-GET: Sending resource " << publicIds[i] | 394 { |
395 LOG(INFO) << "C-GET: Sending resource " << *resource | |
367 << " to modality \"" << originatorAet << "\""; | 396 << " to modality \"" << originatorAet << "\""; |
368 | 397 |
369 std::list<std::string> tmp; | 398 std::list<std::string> tmp; |
370 context_.GetIndex().GetChildInstances(tmp, publicIds[i]); | 399 context_.GetIndex().GetChildInstances(tmp, *resource); |
371 | 400 |
372 instances_.reserve(tmp.size()); | 401 instances_.reserve(tmp.size()); |
373 for (std::list<std::string>::iterator it = tmp.begin(); it != tmp.end(); ++it) | 402 for (std::list<std::string>::iterator it = tmp.begin(); it != tmp.end(); ++it) |
374 { | 403 { |
375 instances_.push_back(*it); | 404 instances_.push_back(*it); |
376 } | 405 } |
377 } | 406 } |
378 | 407 |
379 failedUIDs_.clear(); | 408 failedUIDs_.clear(); |
380 getCancelled_ = OFFalse; | 409 getCancelled_ = OFFalse; |
381 | 410 |
382 nRemaining_ = GetSubOperationCount(); | 411 nRemaining_ = GetSubOperationCount(); |
383 nCompleted_ = 0; | 412 nCompleted_ = 0; |
384 nFailed_ = 0; | 413 nFailed_ = 0; |
385 warningCount_ = 0; | 414 warningCount_ = 0; |
415 timeout_ = timeout; | |
386 | 416 |
387 return true; | 417 return true; |
388 } | 418 } |
389 }; | 419 }; |