comparison OrthancServer/Sources/ServerJobs/DicomGetScuJob.cpp @ 5853:4d932683049d get-scu tip

very first implementation of C-Get SCU
author Alain Mazy <am@orthanc.team>
date Tue, 29 Oct 2024 17:25:49 +0100
parents
children
comparison
equal deleted inserted replaced
5839:7aef730c0859 5853:4d932683049d
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-2023 Osimis S.A., Belgium
6 * Copyright (C) 2024-2024 Orthanc Team SRL, Belgium
7 * Copyright (C) 2021-2024 Sebastien Jodogne, ICTEAM UCLouvain, Belgium
8 *
9 * This program is free software: you can redistribute it and/or
10 * modify it under the terms of the GNU General Public License as
11 * published by the Free Software Foundation, either version 3 of the
12 * License, or (at your option) any later version.
13 *
14 * This program is distributed in the hope that it will be useful, but
15 * WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 * General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with this program. If not, see <http://www.gnu.org/licenses/>.
21 **/
22
23
24 #include "DicomGetScuJob.h"
25
26 #include "../../../OrthancFramework/Sources/DicomParsing/FromDcmtkBridge.h"
27 #include "../../../OrthancFramework/Sources/SerializationToolbox.h"
28 #include "../ServerContext.h"
29 #include <dcmtk/dcmnet/dimse.h>
30
31 static const char* const LOCAL_AET = "LocalAet";
32 static const char* const QUERY = "Query";
33 static const char* const QUERY_FORMAT = "QueryFormat"; // New in 1.9.5
34 static const char* const REMOTE = "Remote";
35 static const char* const TIMEOUT = "Timeout";
36
37 namespace Orthanc
38 {
39 class DicomGetScuJob::Command : public SetOfCommandsJob::ICommand
40 {
41 private:
42 DicomGetScuJob& that_;
43 std::unique_ptr<DicomMap> findAnswer_;
44
45 public:
46 Command(DicomGetScuJob& that,
47 const DicomMap& findAnswer) :
48 that_(that),
49 findAnswer_(findAnswer.Clone())
50 {
51 }
52
53 virtual bool Execute(const std::string& jobId) ORTHANC_OVERRIDE
54 {
55 that_.Retrieve(*findAnswer_);
56 return true;
57 }
58
59 virtual void Serialize(Json::Value& target) const ORTHANC_OVERRIDE
60 {
61 findAnswer_->Serialize(target);
62 }
63 };
64
65
66 class DicomGetScuJob::Unserializer :
67 public SetOfCommandsJob::ICommandUnserializer
68 {
69 private:
70 DicomGetScuJob& that_;
71
72 public:
73 explicit Unserializer(DicomGetScuJob& that) :
74 that_(that)
75 {
76 }
77
78 virtual ICommand* Unserialize(const Json::Value& source) const ORTHANC_OVERRIDE
79 {
80 DicomMap findAnswer;
81 findAnswer.Unserialize(source);
82 return new Command(that_, findAnswer);
83 }
84 };
85
86
87 static uint16_t InstanceReceivedHandler(void* callbackContext,
88 DcmDataset& dataset,
89 const std::string& remoteAet,
90 const std::string& remoteIp,
91 const std::string& calledAet)
92 {
93 // this code is equivalent to OrthancStoreRequestHandler
94 ServerContext* context = reinterpret_cast<ServerContext*>(callbackContext);
95
96 std::unique_ptr<DicomInstanceToStore> toStore(DicomInstanceToStore::CreateFromDcmDataset(dataset));
97
98 if (toStore->GetBufferSize() > 0)
99 {
100 toStore->SetOrigin(DicomInstanceOrigin::FromDicomProtocol
101 (remoteIp.c_str(), remoteAet.c_str(), calledAet.c_str()));
102
103 std::string id;
104 ServerContext::StoreResult result = context->Store(id, *toStore, StoreInstanceMode_Default);
105 return result.GetCStoreStatusCode();
106 }
107
108 return STATUS_STORE_Error_CannotUnderstand;
109 }
110
111 void DicomGetScuJob::Retrieve(const DicomMap& findAnswer)
112 {
113 if (connection_.get() == NULL)
114 {
115 connection_.reset(new DicomControlUserConnection(parameters_));
116 }
117
118 connection_->Get(findAnswer, InstanceReceivedHandler, &context_);
119 }
120
121 void DicomGetScuJob::AddResourceToRetrieve(ResourceType level, const std::string& dicomId)
122 {
123 // TODO-GET: when retrieving a single series, one must provide the StudyInstanceUID too
124 DicomMap item;
125
126 switch (level)
127 {
128 case ResourceType_Patient:
129 item.SetValue(DICOM_TAG_QUERY_RETRIEVE_LEVEL, ResourceTypeToDicomQueryRetrieveLevel(level), false);
130 item.SetValue(DICOM_TAG_PATIENT_ID, dicomId, false);
131 break;
132
133 case ResourceType_Study:
134 item.SetValue(DICOM_TAG_QUERY_RETRIEVE_LEVEL, ResourceTypeToDicomQueryRetrieveLevel(level), false);
135 item.SetValue(DICOM_TAG_STUDY_INSTANCE_UID, dicomId, false);
136 break;
137
138 case ResourceType_Series:
139 item.SetValue(DICOM_TAG_QUERY_RETRIEVE_LEVEL, ResourceTypeToDicomQueryRetrieveLevel(level), false);
140 item.SetValue(DICOM_TAG_SERIES_INSTANCE_UID, dicomId, false);
141 break;
142
143 case ResourceType_Instance:
144 item.SetValue(DICOM_TAG_QUERY_RETRIEVE_LEVEL, ResourceTypeToDicomQueryRetrieveLevel(level), false);
145 item.SetValue(DICOM_TAG_SOP_INSTANCE_UID, dicomId, false);
146 break;
147
148 default:
149 throw OrthancException(ErrorCode_InternalError);
150 }
151 query_.Add(item);
152
153 AddCommand(new Command(*this, item));
154 }
155
156 void DicomGetScuJob::SetLocalAet(const std::string& aet)
157 {
158 if (IsStarted())
159 {
160 throw OrthancException(ErrorCode_BadSequenceOfCalls);
161 }
162 else
163 {
164 parameters_.SetLocalApplicationEntityTitle(aet);
165 }
166 }
167
168
169 void DicomGetScuJob::SetRemoteModality(const RemoteModalityParameters& remote)
170 {
171 if (IsStarted())
172 {
173 throw OrthancException(ErrorCode_BadSequenceOfCalls);
174 }
175 else
176 {
177 parameters_.SetRemoteModality(remote);
178 }
179 }
180
181
182 void DicomGetScuJob::SetTimeout(uint32_t seconds)
183 {
184 if (IsStarted())
185 {
186 throw OrthancException(ErrorCode_BadSequenceOfCalls);
187 }
188 else
189 {
190 parameters_.SetTimeout(seconds);
191 }
192 }
193
194
195 void DicomGetScuJob::Stop(JobStopReason reason)
196 {
197 connection_.reset();
198 }
199
200
201 void DicomGetScuJob::SetQueryFormat(DicomToJsonFormat format)
202 {
203 if (IsStarted())
204 {
205 throw OrthancException(ErrorCode_BadSequenceOfCalls);
206 }
207 else
208 {
209 queryFormat_ = format;
210 }
211 }
212
213
214 void DicomGetScuJob::GetPublicContent(Json::Value& value)
215 {
216 SetOfCommandsJob::GetPublicContent(value);
217
218 value[LOCAL_AET] = parameters_.GetLocalApplicationEntityTitle();
219 value["RemoteAet"] = parameters_.GetRemoteModality().GetApplicationEntityTitle();
220
221 value[QUERY] = Json::objectValue;
222 // query_.ToJson(value[QUERY], queryFormat_);
223 }
224
225
226 DicomGetScuJob::DicomGetScuJob(ServerContext& context,
227 const Json::Value& serialized) :
228 SetOfCommandsJob(new Unserializer(*this), serialized),
229 context_(context),
230 parameters_(DicomAssociationParameters::UnserializeJob(serialized)),
231 // targetAet_(SerializationToolbox::ReadString(serialized, TARGET_AET)),
232 query_(true),
233 queryFormat_(DicomToJsonFormat_Short)
234 {
235 if (serialized.isMember(QUERY))
236 {
237 const Json::Value& query = serialized[QUERY];
238 if (query.type() == Json::arrayValue)
239 {
240 for (Json::Value::ArrayIndex i = 0; i < query.size(); i++)
241 {
242 DicomMap item;
243 FromDcmtkBridge::FromJson(item, query[i]);
244 // AddToQuery(query_, item);
245 }
246 }
247 }
248
249 if (serialized.isMember(QUERY_FORMAT))
250 {
251 queryFormat_ = StringToDicomToJsonFormat(SerializationToolbox::ReadString(serialized, QUERY_FORMAT));
252 }
253 }
254
255
256 bool DicomGetScuJob::Serialize(Json::Value& target)
257 {
258 if (!SetOfCommandsJob::Serialize(target))
259 {
260 return false;
261 }
262 else
263 {
264 parameters_.SerializeJob(target);
265 // target[TARGET_AET] = targetAet_;
266
267 // "Short" is for compatibility with Orthanc <= 1.9.4
268 target[QUERY] = Json::objectValue;
269 // query_.ToJson(target[QUERY], DicomToJsonFormat_Short);
270
271 target[QUERY_FORMAT] = EnumerationToString(queryFormat_);
272
273 return true;
274 }
275 }
276 }