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 "DicomMoveScuJob.h"
35 
36 #include "../../../OrthancFramework/Sources/SerializationToolbox.h"
37 #include "../ServerContext.h"
38 
39 static const char* const LOCAL_AET = "LocalAet";
40 static const char* const TARGET_AET = "TargetAet";
41 static const char* const REMOTE = "Remote";
42 static const char* const QUERY = "Query";
43 static const char* const TIMEOUT = "Timeout";
44 
45 namespace Orthanc
46 {
47   class DicomMoveScuJob::Command : public SetOfCommandsJob::ICommand
48   {
49   private:
50     DicomMoveScuJob&           that_;
51     std::unique_ptr<DicomMap>  findAnswer_;
52 
53   public:
Command(DicomMoveScuJob & that,const DicomMap & findAnswer)54     Command(DicomMoveScuJob& that,
55             const DicomMap&  findAnswer) :
56       that_(that),
57       findAnswer_(findAnswer.Clone())
58     {
59     }
60 
Execute(const std::string & jobId)61     virtual bool Execute(const std::string& jobId) ORTHANC_OVERRIDE
62     {
63       that_.Retrieve(*findAnswer_);
64       return true;
65     }
66 
Serialize(Json::Value & target) const67     virtual void Serialize(Json::Value& target) const ORTHANC_OVERRIDE
68     {
69       findAnswer_->Serialize(target);
70     }
71   };
72 
73 
74   class DicomMoveScuJob::Unserializer :
75     public SetOfCommandsJob::ICommandUnserializer
76   {
77   private:
78     DicomMoveScuJob&   that_;
79 
80   public:
Unserializer(DicomMoveScuJob & that)81     explicit Unserializer(DicomMoveScuJob&  that) :
82       that_(that)
83     {
84     }
85 
Unserialize(const Json::Value & source) const86     virtual ICommand* Unserialize(const Json::Value& source) const ORTHANC_OVERRIDE
87     {
88       DicomMap findAnswer;
89       findAnswer.Unserialize(source);
90       return new Command(that_, findAnswer);
91     }
92   };
93 
94 
95 
Retrieve(const DicomMap & findAnswer)96   void DicomMoveScuJob::Retrieve(const DicomMap& findAnswer)
97   {
98     if (connection_.get() == NULL)
99     {
100       connection_.reset(new DicomControlUserConnection(parameters_));
101     }
102 
103     connection_->Move(targetAet_, findAnswer);
104   }
105 
106 
AddTagIfString(Json::Value & target,const DicomMap & answer,const DicomTag & tag)107   static void AddTagIfString(Json::Value& target,
108                              const DicomMap& answer,
109                              const DicomTag& tag)
110   {
111     const DicomValue* value = answer.TestAndGetValue(tag);
112     if (value != NULL &&
113         !value->IsNull() &&
114         !value->IsBinary())
115     {
116       target[tag.Format()] = value->GetContent();
117     }
118   }
119 
120 
AddFindAnswer(const DicomMap & answer)121   void DicomMoveScuJob::AddFindAnswer(const DicomMap& answer)
122   {
123     assert(query_.type() == Json::arrayValue);
124 
125     // Copy the identifiers tags, if they exist
126     Json::Value item = Json::objectValue;
127     AddTagIfString(item, answer, DICOM_TAG_QUERY_RETRIEVE_LEVEL);
128     AddTagIfString(item, answer, DICOM_TAG_PATIENT_ID);
129     AddTagIfString(item, answer, DICOM_TAG_STUDY_INSTANCE_UID);
130     AddTagIfString(item, answer, DICOM_TAG_SERIES_INSTANCE_UID);
131     AddTagIfString(item, answer, DICOM_TAG_SOP_INSTANCE_UID);
132     AddTagIfString(item, answer, DICOM_TAG_ACCESSION_NUMBER);
133     query_.append(item);
134 
135     AddCommand(new Command(*this, answer));
136   }
137 
138 
AddFindAnswer(QueryRetrieveHandler & query,size_t i)139   void DicomMoveScuJob::AddFindAnswer(QueryRetrieveHandler& query,
140                                       size_t i)
141   {
142     DicomMap answer;
143     query.GetAnswer(answer, i);
144     AddFindAnswer(answer);
145   }
146 
147 
SetLocalAet(const std::string & aet)148   void DicomMoveScuJob::SetLocalAet(const std::string& aet)
149   {
150     if (IsStarted())
151     {
152       throw OrthancException(ErrorCode_BadSequenceOfCalls);
153     }
154     else
155     {
156       parameters_.SetLocalApplicationEntityTitle(aet);
157     }
158   }
159 
160 
SetTargetAet(const std::string & aet)161   void DicomMoveScuJob::SetTargetAet(const std::string& aet)
162   {
163     if (IsStarted())
164     {
165       throw OrthancException(ErrorCode_BadSequenceOfCalls);
166     }
167     else
168     {
169       targetAet_ = aet;
170     }
171   }
172 
173 
SetRemoteModality(const RemoteModalityParameters & remote)174   void DicomMoveScuJob::SetRemoteModality(const RemoteModalityParameters& remote)
175   {
176     if (IsStarted())
177     {
178       throw OrthancException(ErrorCode_BadSequenceOfCalls);
179     }
180     else
181     {
182       parameters_.SetRemoteModality(remote);
183     }
184   }
185 
186 
SetTimeout(uint32_t seconds)187   void DicomMoveScuJob::SetTimeout(uint32_t seconds)
188   {
189     if (IsStarted())
190     {
191       throw OrthancException(ErrorCode_BadSequenceOfCalls);
192     }
193     else
194     {
195       parameters_.SetTimeout(seconds);
196     }
197   }
198 
199 
Stop(JobStopReason reason)200   void DicomMoveScuJob::Stop(JobStopReason reason)
201   {
202     connection_.reset();
203   }
204 
205 
GetPublicContent(Json::Value & value)206   void DicomMoveScuJob::GetPublicContent(Json::Value& value)
207   {
208     SetOfCommandsJob::GetPublicContent(value);
209 
210     value["LocalAet"] = parameters_.GetLocalApplicationEntityTitle();
211     value["RemoteAet"] = parameters_.GetRemoteModality().GetApplicationEntityTitle();
212     value["Query"] = query_;
213   }
214 
215 
DicomMoveScuJob(ServerContext & context,const Json::Value & serialized)216   DicomMoveScuJob::DicomMoveScuJob(ServerContext& context,
217                                    const Json::Value& serialized) :
218     SetOfCommandsJob(new Unserializer(*this), serialized),
219     context_(context),
220     parameters_(DicomAssociationParameters::UnserializeJob(serialized)),
221     targetAet_(SerializationToolbox::ReadString(serialized, TARGET_AET)),
222     query_(Json::arrayValue)
223   {
224     if (serialized.isMember(QUERY) &&
225         serialized[QUERY].type() == Json::arrayValue)
226     {
227       query_ = serialized[QUERY];
228     }
229   }
230 
231 
Serialize(Json::Value & target)232   bool DicomMoveScuJob::Serialize(Json::Value& target)
233   {
234     if (!SetOfCommandsJob::Serialize(target))
235     {
236       return false;
237     }
238     else
239     {
240       parameters_.SerializeJob(target);
241       target[TARGET_AET] = targetAet_;
242       target[QUERY] = query_;
243       return true;
244     }
245   }
246 }
247