comparison OrthancServer/Sources/ServerJobs/DicomModalityStoreJob.cpp @ 4044:d25f4c0fa160 framework

splitting code into OrthancFramework and OrthancServer
author Sebastien Jodogne <s.jodogne@gmail.com>
date Wed, 10 Jun 2020 20:30:34 +0200
parents OrthancServer/ServerJobs/DicomModalityStoreJob.cpp@8f7ad4989fec
children 05b8fd21089c
comparison
equal deleted inserted replaced
4043:6c6239aec462 4044:d25f4c0fa160
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 "../PrecompiledHeadersServer.h"
35 #include "DicomModalityStoreJob.h"
36
37 #include "../../Core/Compatibility.h"
38 #include "../../Core/DicomNetworking/DicomAssociation.h"
39 #include "../../Core/Logging.h"
40 #include "../../Core/SerializationToolbox.h"
41 #include "../ServerContext.h"
42 #include "../StorageCommitmentReports.h"
43
44
45 namespace Orthanc
46 {
47 void DicomModalityStoreJob::OpenConnection()
48 {
49 if (connection_.get() == NULL)
50 {
51 connection_.reset(new DicomStoreUserConnection(parameters_));
52 }
53 }
54
55
56 bool DicomModalityStoreJob::HandleInstance(const std::string& instance)
57 {
58 assert(IsStarted());
59 OpenConnection();
60
61 LOG(INFO) << "Sending instance " << instance << " to modality \""
62 << parameters_.GetRemoteModality().GetApplicationEntityTitle() << "\"";
63
64 std::string dicom;
65
66 try
67 {
68 context_.ReadDicom(dicom, instance);
69 }
70 catch (OrthancException& e)
71 {
72 LOG(WARNING) << "An instance was removed after the job was issued: " << instance;
73 return false;
74 }
75
76 std::string sopClassUid, sopInstanceUid;
77 context_.StoreWithTranscoding(sopClassUid, sopInstanceUid, *connection_, dicom,
78 HasMoveOriginator(), moveOriginatorAet_, moveOriginatorId_);
79
80 if (storageCommitment_)
81 {
82 sopClassUids_.push_back(sopClassUid);
83 sopInstanceUids_.push_back(sopInstanceUid);
84
85 if (sopClassUids_.size() != sopInstanceUids_.size() ||
86 sopClassUids_.size() > GetInstancesCount())
87 {
88 throw OrthancException(ErrorCode_InternalError);
89 }
90
91 if (sopClassUids_.size() == GetInstancesCount())
92 {
93 assert(IsStarted());
94 connection_.reset(NULL);
95
96 const std::string& remoteAet = parameters_.GetRemoteModality().GetApplicationEntityTitle();
97
98 LOG(INFO) << "Sending storage commitment request to modality: " << remoteAet;
99
100 // Create a "pending" storage commitment report BEFORE the
101 // actual SCU call in order to avoid race conditions
102 context_.GetStorageCommitmentReports().Store(
103 transactionUid_, new StorageCommitmentReports::Report(remoteAet));
104
105 std::vector<std::string> a(sopClassUids_.begin(), sopClassUids_.end());
106 std::vector<std::string> b(sopInstanceUids_.begin(), sopInstanceUids_.end());
107
108 DicomAssociation::RequestStorageCommitment(parameters_, transactionUid_, a, b);
109 }
110 }
111
112 //boost::this_thread::sleep(boost::posix_time::milliseconds(500));
113
114 return true;
115 }
116
117
118 bool DicomModalityStoreJob::HandleTrailingStep()
119 {
120 throw OrthancException(ErrorCode_InternalError);
121 }
122
123
124 DicomModalityStoreJob::DicomModalityStoreJob(ServerContext& context) :
125 context_(context),
126 moveOriginatorId_(0), // By default, not a C-MOVE
127 storageCommitment_(false) // By default, no storage commitment
128 {
129 ResetStorageCommitment();
130 }
131
132
133 void DicomModalityStoreJob::SetLocalAet(const std::string& aet)
134 {
135 if (IsStarted())
136 {
137 throw OrthancException(ErrorCode_BadSequenceOfCalls);
138 }
139 else
140 {
141 parameters_.SetLocalApplicationEntityTitle(aet);
142 }
143 }
144
145
146 void DicomModalityStoreJob::SetRemoteModality(const RemoteModalityParameters& remote)
147 {
148 if (IsStarted())
149 {
150 throw OrthancException(ErrorCode_BadSequenceOfCalls);
151 }
152 else
153 {
154 parameters_.SetRemoteModality(remote);
155 }
156 }
157
158
159 void DicomModalityStoreJob::SetTimeout(uint32_t seconds)
160 {
161 if (IsStarted())
162 {
163 throw OrthancException(ErrorCode_BadSequenceOfCalls);
164 }
165 else
166 {
167 parameters_.SetTimeout(seconds);
168 }
169 }
170
171
172 const std::string& DicomModalityStoreJob::GetMoveOriginatorAet() const
173 {
174 if (HasMoveOriginator())
175 {
176 return moveOriginatorAet_;
177 }
178 else
179 {
180 throw OrthancException(ErrorCode_BadSequenceOfCalls);
181 }
182 }
183
184
185 uint16_t DicomModalityStoreJob::GetMoveOriginatorId() const
186 {
187 if (HasMoveOriginator())
188 {
189 return moveOriginatorId_;
190 }
191 else
192 {
193 throw OrthancException(ErrorCode_BadSequenceOfCalls);
194 }
195 }
196
197
198 void DicomModalityStoreJob::SetMoveOriginator(const std::string& aet,
199 int id)
200 {
201 if (IsStarted())
202 {
203 throw OrthancException(ErrorCode_BadSequenceOfCalls);
204 }
205 else if (id < 0 ||
206 id >= 65536)
207 {
208 throw OrthancException(ErrorCode_ParameterOutOfRange);
209 }
210 else
211 {
212 moveOriginatorId_ = static_cast<uint16_t>(id);
213 moveOriginatorAet_ = aet;
214 }
215 }
216
217 void DicomModalityStoreJob::Stop(JobStopReason reason) // For pausing jobs
218 {
219 connection_.reset(NULL);
220 }
221
222
223 void DicomModalityStoreJob::ResetStorageCommitment()
224 {
225 if (storageCommitment_)
226 {
227 transactionUid_ = Toolbox::GenerateDicomPrivateUniqueIdentifier();
228 sopClassUids_.clear();
229 sopInstanceUids_.clear();
230 }
231 }
232
233
234 void DicomModalityStoreJob::Reset()
235 {
236 SetOfInstancesJob::Reset();
237
238 /**
239 * "After the N-EVENT-REPORT has been sent, the Transaction UID is
240 * no longer active and shall not be reused for other
241 * transactions." => Need to reset the transaction UID here
242 * http://dicom.nema.org/medical/dicom/2019a/output/chtml/part04/sect_J.3.3.html
243 **/
244 ResetStorageCommitment();
245 }
246
247
248 void DicomModalityStoreJob::EnableStorageCommitment(bool enabled)
249 {
250 storageCommitment_ = enabled;
251 ResetStorageCommitment();
252 }
253
254
255 void DicomModalityStoreJob::GetPublicContent(Json::Value& value)
256 {
257 SetOfInstancesJob::GetPublicContent(value);
258
259 value["LocalAet"] = parameters_.GetLocalApplicationEntityTitle();
260 value["RemoteAet"] = parameters_.GetRemoteModality().GetApplicationEntityTitle();
261
262 if (HasMoveOriginator())
263 {
264 value["MoveOriginatorAET"] = GetMoveOriginatorAet();
265 value["MoveOriginatorID"] = GetMoveOriginatorId();
266 }
267
268 if (storageCommitment_)
269 {
270 value["StorageCommitmentTransactionUID"] = transactionUid_;
271 }
272 }
273
274
275 static const char* MOVE_ORIGINATOR_AET = "MoveOriginatorAet";
276 static const char* MOVE_ORIGINATOR_ID = "MoveOriginatorId";
277 static const char* STORAGE_COMMITMENT = "StorageCommitment";
278
279
280 DicomModalityStoreJob::DicomModalityStoreJob(ServerContext& context,
281 const Json::Value& serialized) :
282 SetOfInstancesJob(serialized),
283 context_(context)
284 {
285 moveOriginatorAet_ = SerializationToolbox::ReadString(serialized, MOVE_ORIGINATOR_AET);
286 moveOriginatorId_ = static_cast<uint16_t>
287 (SerializationToolbox::ReadUnsignedInteger(serialized, MOVE_ORIGINATOR_ID));
288 EnableStorageCommitment(SerializationToolbox::ReadBoolean(serialized, STORAGE_COMMITMENT));
289
290 parameters_ = DicomAssociationParameters::UnserializeJob(serialized);
291 }
292
293
294 bool DicomModalityStoreJob::Serialize(Json::Value& target)
295 {
296 if (!SetOfInstancesJob::Serialize(target))
297 {
298 return false;
299 }
300 else
301 {
302 parameters_.SerializeJob(target);
303 target[MOVE_ORIGINATOR_AET] = moveOriginatorAet_;
304 target[MOVE_ORIGINATOR_ID] = moveOriginatorId_;
305 target[STORAGE_COMMITMENT] = storageCommitment_;
306 return true;
307 }
308 }
309 }