Mercurial > hg > orthanc
comparison OrthancServer/ServerJobs/StorageCommitmentScpJob.cpp @ 3657:115f82775c46 storage-commitment
handling of storage commitment failure reasons
author | Sebastien Jodogne <s.jodogne@gmail.com> |
---|---|
date | Mon, 10 Feb 2020 14:53:36 +0100 |
parents | fddf3fc82362 |
children | 2d90dd30858c |
comparison
equal
deleted
inserted
replaced
3656:cccd97333e3d | 3657:115f82775c46 |
---|---|
42 #include "../ServerContext.h" | 42 #include "../ServerContext.h" |
43 | 43 |
44 | 44 |
45 static const char* ANSWER = "Answer"; | 45 static const char* ANSWER = "Answer"; |
46 static const char* CALLED_AET = "CalledAet"; | 46 static const char* CALLED_AET = "CalledAet"; |
47 static const char* FAILED_SOP_CLASS_UIDS = "FailedSopClassUids"; | |
48 static const char* FAILED_SOP_INSTANCE_UIDS = "FailedSopInstanceUids"; | |
49 static const char* LOOKUP = "Lookup"; | 47 static const char* LOOKUP = "Lookup"; |
50 static const char* REMOTE_MODALITY = "RemoteModality"; | 48 static const char* REMOTE_MODALITY = "RemoteModality"; |
51 static const char* SOP_CLASS_UID = "SopClassUid"; | 49 static const char* SOP_CLASS_UID = "SopClassUid"; |
52 static const char* SOP_INSTANCE_UID = "SopInstanceUid"; | 50 static const char* SOP_INSTANCE_UID = "SopInstanceUid"; |
53 static const char* SUCCESS_SOP_CLASS_UIDS = "SuccessSopClassUids"; | |
54 static const char* SUCCESS_SOP_INSTANCE_UIDS = "SuccessSopInstanceUids"; | |
55 static const char* TRANSACTION_UID = "TransactionUid"; | 51 static const char* TRANSACTION_UID = "TransactionUid"; |
56 static const char* TYPE = "Type"; | 52 static const char* TYPE = "Type"; |
57 | 53 |
58 | 54 |
59 | 55 |
60 namespace Orthanc | 56 namespace Orthanc |
61 { | 57 { |
62 class StorageCommitmentScpJob::LookupCommand : public SetOfCommandsJob::ICommand | 58 class StorageCommitmentScpJob::StorageCommitmentCommand : public SetOfCommandsJob::ICommand |
59 { | |
60 public: | |
61 virtual bool IsAnswer() const = 0; | |
62 }; | |
63 | |
64 | |
65 class StorageCommitmentScpJob::LookupCommand : public StorageCommitmentCommand | |
63 { | 66 { |
64 private: | 67 private: |
65 StorageCommitmentScpJob& that_; | 68 ServerContext& context_; |
66 std::string sopClassUid_; | 69 bool hasFailureReason_; |
67 std::string sopInstanceUid_; | 70 std::string sopClassUid_; |
71 std::string sopInstanceUid_; | |
72 StorageCommitmentFailureReason failureReason_; | |
68 | 73 |
69 public: | 74 public: |
70 LookupCommand(StorageCommitmentScpJob& that, | 75 LookupCommand(ServerContext& context, |
71 const std::string& sopClassUid, | 76 const std::string& sopClassUid, |
72 const std::string& sopInstanceUid) : | 77 const std::string& sopInstanceUid) : |
73 that_(that), | 78 context_(context), |
79 hasFailureReason_(false), | |
74 sopClassUid_(sopClassUid), | 80 sopClassUid_(sopClassUid), |
75 sopInstanceUid_(sopInstanceUid) | 81 sopInstanceUid_(sopInstanceUid) |
76 { | 82 { |
77 } | 83 } |
78 | 84 |
85 virtual bool IsAnswer() const | |
86 { | |
87 return false; | |
88 } | |
89 | |
79 virtual bool Execute() | 90 virtual bool Execute() |
80 { | 91 { |
81 that_.LookupInstance(sopClassUid_, sopInstanceUid_); | 92 if (hasFailureReason_) |
93 { | |
94 throw OrthancException(ErrorCode_BadSequenceOfCalls); | |
95 } | |
96 | |
97 bool success = false; | |
98 | |
99 try | |
100 { | |
101 std::vector<std::string> orthancId; | |
102 context_.GetIndex().LookupIdentifierExact(orthancId, ResourceType_Instance, DICOM_TAG_SOP_INSTANCE_UID, sopInstanceUid_); | |
103 | |
104 if (orthancId.size() == 1) | |
105 { | |
106 std::string a, b; | |
107 | |
108 // Make sure that the DICOM file can be re-read by DCMTK | |
109 // from the file storage, and that the actual SOP | |
110 // class/instance UIDs do match | |
111 ServerContext::DicomCacheLocker locker(context_, orthancId[0]); | |
112 if (locker.GetDicom().GetTagValue(a, DICOM_TAG_SOP_CLASS_UID) && | |
113 locker.GetDicom().GetTagValue(b, DICOM_TAG_SOP_INSTANCE_UID) && | |
114 a == sopClassUid_ && | |
115 b == sopInstanceUid_) | |
116 { | |
117 success = true; | |
118 } | |
119 } | |
120 } | |
121 catch (OrthancException&) | |
122 { | |
123 } | |
124 | |
125 LOG(INFO) << " Storage commitment SCP job: " << (success ? "Success" : "Failure") | |
126 << " while looking for " << sopClassUid_ << " / " << sopInstanceUid_; | |
127 | |
128 failureReason_ = (success ? | |
129 StorageCommitmentFailureReason_Success : | |
130 StorageCommitmentFailureReason_NoSuchObjectInstance /* 0x0112 == 274 */); | |
131 hasFailureReason_ = true; | |
132 | |
82 return true; | 133 return true; |
134 } | |
135 | |
136 const std::string& GetSopClassUid() const | |
137 { | |
138 return sopClassUid_; | |
139 } | |
140 | |
141 const std::string& GetSopInstanceUid() const | |
142 { | |
143 return sopInstanceUid_; | |
144 } | |
145 | |
146 StorageCommitmentFailureReason GetFailureReason() const | |
147 { | |
148 if (hasFailureReason_) | |
149 { | |
150 return failureReason_; | |
151 } | |
152 else | |
153 { | |
154 throw OrthancException(ErrorCode_BadSequenceOfCalls); | |
155 } | |
83 } | 156 } |
84 | 157 |
85 virtual void Serialize(Json::Value& target) const | 158 virtual void Serialize(Json::Value& target) const |
86 { | 159 { |
87 target = Json::objectValue; | 160 target = Json::objectValue; |
90 target[SOP_INSTANCE_UID] = sopInstanceUid_; | 163 target[SOP_INSTANCE_UID] = sopInstanceUid_; |
91 } | 164 } |
92 }; | 165 }; |
93 | 166 |
94 | 167 |
95 class StorageCommitmentScpJob::AnswerCommand : public SetOfCommandsJob::ICommand | 168 class StorageCommitmentScpJob::AnswerCommand : public StorageCommitmentCommand |
96 { | 169 { |
97 private: | 170 private: |
98 StorageCommitmentScpJob& that_; | 171 StorageCommitmentScpJob& that_; |
99 | 172 |
100 public: | 173 public: |
109 { | 182 { |
110 that_.ready_ = true; | 183 that_.ready_ = true; |
111 } | 184 } |
112 } | 185 } |
113 | 186 |
187 virtual bool IsAnswer() const | |
188 { | |
189 return true; | |
190 } | |
191 | |
114 virtual bool Execute() | 192 virtual bool Execute() |
115 { | 193 { |
116 that_.Answer(); | 194 that_.Answer(); |
117 return true; | 195 return true; |
118 } | 196 } |
126 | 204 |
127 | 205 |
128 class StorageCommitmentScpJob::Unserializer : public SetOfCommandsJob::ICommandUnserializer | 206 class StorageCommitmentScpJob::Unserializer : public SetOfCommandsJob::ICommandUnserializer |
129 { | 207 { |
130 private: | 208 private: |
131 StorageCommitmentScpJob& that_; | 209 StorageCommitmentScpJob& that_; |
210 ServerContext& context_; | |
132 | 211 |
133 public: | 212 public: |
134 Unserializer(StorageCommitmentScpJob& that) : | 213 Unserializer(StorageCommitmentScpJob& that, |
135 that_(that) | 214 ServerContext& context) : |
215 that_(that), | |
216 context_(context) | |
136 { | 217 { |
137 that_.ready_ = false; | 218 that_.ready_ = false; |
138 } | 219 } |
139 | 220 |
140 virtual ICommand* Unserialize(const Json::Value& source) const | 221 virtual ICommand* Unserialize(const Json::Value& source) const |
141 { | 222 { |
142 const std::string type = SerializationToolbox::ReadString(source, TYPE); | 223 const std::string type = SerializationToolbox::ReadString(source, TYPE); |
143 | 224 |
144 if (type == LOOKUP) | 225 if (type == LOOKUP) |
145 { | 226 { |
146 return new LookupCommand(that_, | 227 return new LookupCommand(context_, |
147 SerializationToolbox::ReadString(source, SOP_CLASS_UID), | 228 SerializationToolbox::ReadString(source, SOP_CLASS_UID), |
148 SerializationToolbox::ReadString(source, SOP_INSTANCE_UID)); | 229 SerializationToolbox::ReadString(source, SOP_INSTANCE_UID)); |
149 } | 230 } |
150 else if (type == ANSWER) | 231 else if (type == ANSWER) |
151 { | 232 { |
157 } | 238 } |
158 } | 239 } |
159 }; | 240 }; |
160 | 241 |
161 | 242 |
162 void StorageCommitmentScpJob::LookupInstance(const std::string& sopClassUid, | 243 void StorageCommitmentScpJob::Answer() |
163 const std::string& sopInstanceUid) | 244 { |
164 { | 245 LOG(INFO) << " Storage commitment SCP job: Sending answer"; |
165 bool success = false; | 246 |
166 | 247 const size_t n = GetCommandsCount(); |
167 try | 248 |
168 { | 249 if (n == 0) |
169 std::vector<std::string> orthancId; | 250 { |
170 context_.GetIndex().LookupIdentifierExact(orthancId, ResourceType_Instance, DICOM_TAG_SOP_INSTANCE_UID, sopInstanceUid); | 251 throw OrthancException(ErrorCode_InternalError); |
171 | 252 } |
172 if (orthancId.size() == 1) | 253 |
173 { | 254 std::vector<std::string> sopClassUids, sopInstanceUids; |
174 std::string a, b; | 255 std::vector<StorageCommitmentFailureReason> failureReasons; |
175 | 256 |
176 // Make sure that the DICOM file can be re-read by DCMTK | 257 sopClassUids.reserve(n); |
177 // from the file storage, and that the actual SOP | 258 sopInstanceUids.reserve(n); |
178 // class/instance UIDs do match | 259 failureReasons.reserve(n); |
179 ServerContext::DicomCacheLocker locker(context_, orthancId[0]); | 260 |
180 if (locker.GetDicom().GetTagValue(a, DICOM_TAG_SOP_CLASS_UID) && | 261 for (size_t i = 0; i < n; i++) |
181 locker.GetDicom().GetTagValue(b, DICOM_TAG_SOP_INSTANCE_UID) && | 262 { |
182 a == sopClassUid && | 263 const StorageCommitmentCommand& command = dynamic_cast<const StorageCommitmentCommand&>(GetCommand(i)); |
183 b == sopInstanceUid) | 264 |
265 if (i == n - 1) | |
266 { | |
267 if (!command.IsAnswer()) | |
184 { | 268 { |
185 success = true; | 269 throw OrthancException(ErrorCode_InternalError); |
186 } | 270 } |
187 } | 271 } |
188 } | 272 else |
189 catch (OrthancException&) | 273 { |
190 { | 274 if (command.IsAnswer()) |
191 } | 275 { |
192 | 276 throw OrthancException(ErrorCode_InternalError); |
193 LOG(INFO) << " Storage commitment SCP job: " << (success ? "Success" : "Failure") | 277 } |
194 << " while looking for " << sopClassUid << " / " << sopInstanceUid; | 278 |
195 | 279 const LookupCommand& lookup = dynamic_cast<const LookupCommand&>(command); |
196 if (success) | 280 |
197 { | 281 sopClassUids.push_back(lookup.GetSopClassUid()); |
198 successSopClassUids_.push_back(sopClassUid); | 282 sopInstanceUids.push_back(lookup.GetSopInstanceUid()); |
199 successSopInstanceUids_.push_back(sopInstanceUid); | 283 failureReasons.push_back(lookup.GetFailureReason()); |
200 } | 284 } |
201 else | 285 } |
202 { | |
203 failedSopClassUids_.push_back(sopClassUid); | |
204 failedSopInstanceUids_.push_back(sopInstanceUid); | |
205 } | |
206 } | |
207 | |
208 | |
209 void StorageCommitmentScpJob::Answer() | |
210 { | |
211 LOG(INFO) << " Storage commitment SCP job: Sending answer"; | |
212 | 286 |
213 DicomUserConnection scu(calledAet_, remoteModality_); | 287 DicomUserConnection scu(calledAet_, remoteModality_); |
214 scu.ReportStorageCommitment(transactionUid_, successSopClassUids_, successSopInstanceUids_, | 288 scu.ReportStorageCommitment(transactionUid_, sopClassUids, sopInstanceUids, failureReasons); |
215 failedSopClassUids_, failedSopInstanceUids_); | |
216 } | 289 } |
217 | 290 |
218 | 291 |
219 StorageCommitmentScpJob::StorageCommitmentScpJob(ServerContext& context, | 292 StorageCommitmentScpJob::StorageCommitmentScpJob(ServerContext& context, |
220 const std::string& transactionUid, | 293 const std::string& transactionUid, |
243 { | 316 { |
244 throw OrthancException(ErrorCode_BadSequenceOfCalls); | 317 throw OrthancException(ErrorCode_BadSequenceOfCalls); |
245 } | 318 } |
246 else | 319 else |
247 { | 320 { |
248 AddCommand(new LookupCommand(*this, sopClassUid, sopInstanceUid)); | 321 AddCommand(new LookupCommand(context_, sopClassUid, sopInstanceUid)); |
249 } | 322 } |
250 } | 323 } |
251 | 324 |
252 | 325 |
253 void StorageCommitmentScpJob::MarkAsReady() | 326 void StorageCommitmentScpJob::MarkAsReady() |
264 value["RemoteAet"] = remoteModality_.GetApplicationEntityTitle(); | 337 value["RemoteAet"] = remoteModality_.GetApplicationEntityTitle(); |
265 value["TransactionUid"] = transactionUid_; | 338 value["TransactionUid"] = transactionUid_; |
266 } | 339 } |
267 | 340 |
268 | 341 |
269 | |
270 StorageCommitmentScpJob::StorageCommitmentScpJob(ServerContext& context, | 342 StorageCommitmentScpJob::StorageCommitmentScpJob(ServerContext& context, |
271 const Json::Value& serialized) : | 343 const Json::Value& serialized) : |
272 SetOfCommandsJob(new Unserializer(*this), serialized), | 344 SetOfCommandsJob(new Unserializer(*this, context), serialized), |
273 context_(context) | 345 context_(context) |
274 { | 346 { |
275 transactionUid_ = SerializationToolbox::ReadString(serialized, TRANSACTION_UID); | 347 transactionUid_ = SerializationToolbox::ReadString(serialized, TRANSACTION_UID); |
276 remoteModality_ = RemoteModalityParameters(serialized[REMOTE_MODALITY]); | 348 remoteModality_ = RemoteModalityParameters(serialized[REMOTE_MODALITY]); |
277 calledAet_ = SerializationToolbox::ReadString(serialized, CALLED_AET); | 349 calledAet_ = SerializationToolbox::ReadString(serialized, CALLED_AET); |
278 SerializationToolbox::ReadListOfStrings(successSopClassUids_, serialized, SUCCESS_SOP_CLASS_UIDS); | |
279 SerializationToolbox::ReadListOfStrings(successSopInstanceUids_, serialized, SUCCESS_SOP_INSTANCE_UIDS); | |
280 SerializationToolbox::ReadListOfStrings(failedSopClassUids_, serialized, FAILED_SOP_CLASS_UIDS); | |
281 SerializationToolbox::ReadListOfStrings(failedSopInstanceUids_, serialized, FAILED_SOP_INSTANCE_UIDS); | |
282 } | 350 } |
283 | 351 |
284 | 352 |
285 bool StorageCommitmentScpJob::Serialize(Json::Value& target) | 353 bool StorageCommitmentScpJob::Serialize(Json::Value& target) |
286 { | 354 { |
291 else | 359 else |
292 { | 360 { |
293 target[TRANSACTION_UID] = transactionUid_; | 361 target[TRANSACTION_UID] = transactionUid_; |
294 remoteModality_.Serialize(target[REMOTE_MODALITY], true /* force advanced format */); | 362 remoteModality_.Serialize(target[REMOTE_MODALITY], true /* force advanced format */); |
295 target[CALLED_AET] = calledAet_; | 363 target[CALLED_AET] = calledAet_; |
296 SerializationToolbox::WriteListOfStrings(target, successSopClassUids_, SUCCESS_SOP_CLASS_UIDS); | |
297 SerializationToolbox::WriteListOfStrings(target, successSopInstanceUids_, SUCCESS_SOP_INSTANCE_UIDS); | |
298 SerializationToolbox::WriteListOfStrings(target, failedSopClassUids_, FAILED_SOP_CLASS_UIDS); | |
299 SerializationToolbox::WriteListOfStrings(target, failedSopInstanceUids_, FAILED_SOP_INSTANCE_UIDS); | |
300 return true; | 364 return true; |
301 } | 365 } |
302 } | 366 } |
303 } | 367 } |