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 }