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