comparison OrthancServer/Plugins/Samples/MultitenantDicom/MoveRequestHandler.cpp @ 5273:7cb1b851f5c8

Added a sample plugin bringing multitenant DICOM support through labels
author Sebastien Jodogne <s.jodogne@gmail.com>
date Fri, 14 Apr 2023 11:49:24 +0200
parents
children 49477780e25a
comparison
equal deleted inserted replaced
5272:a45e8b6115f6 5273:7cb1b851f5c8
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) 2021-2023 Sebastien Jodogne, ICTEAM UCLouvain, Belgium
7 *
8 * This program is free software: you can redistribute it and/or
9 * modify it under the terms of the GNU Lesser General Public License
10 * as published by the Free Software Foundation, either version 3 of
11 * the License, or (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful, but
14 * WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Lesser General Public License for more details.
17 *
18 * You should have received a copy of the GNU Lesser General Public
19 * License along with this program. If not, see
20 * <http://www.gnu.org/licenses/>.
21 **/
22
23
24 #include "MoveRequestHandler.h"
25
26 #include "PluginToolbox.h"
27
28 #include "../../../../OrthancFramework/Sources/OrthancException.h"
29 #include "../../../../OrthancFramework/Sources/Toolbox.h"
30
31 #include "../Common/OrthancPluginCppWrapper.h"
32
33
34 class MoveRequestHandler::Iterator : public Orthanc::IMoveRequestIterator
35 {
36 private:
37 std::string targetModality_;
38 Json::Value body_;
39 bool done_;
40
41 public:
42 Iterator(const std::string& targetModality,
43 const Json::Value& body) :
44 targetModality_(targetModality),
45 body_(body),
46 done_(false)
47 {
48 }
49
50 unsigned int GetSubOperationCount() const
51 {
52 return 1;
53 }
54
55 Status DoNext()
56 {
57 Json::Value answer;
58
59 if (done_)
60 {
61 return Status_Failure;
62 }
63 else if (OrthancPlugins::RestApiPost(answer, "/modalities/" + targetModality_ + "/store", body_, false))
64 {
65 done_ = true;
66 return Status_Success;
67 }
68 else
69 {
70 done_ = true;
71 return Status_Failure;
72 }
73 }
74 };
75
76
77 void MoveRequestHandler::ExecuteLookup(std::set<std::string>& publicIds,
78 Orthanc::ResourceType level,
79 const Orthanc::DicomTag& tag,
80 const std::string& value) const
81 {
82 std::vector<std::string> tokens;
83 Orthanc::Toolbox::TokenizeString(tokens, value, '\\');
84
85 for (size_t i = 0; i < tokens.size(); i++)
86 {
87 if (!tokens[i].empty())
88 {
89 Json::Value request = Json::objectValue;
90 request["Level"] = Orthanc::EnumerationToString(level);
91 request["Query"][tag.Format()] = tokens[i];
92 PluginToolbox::AddLabelsToFindRequest(request, labels_, constraint_);
93
94 Json::Value response;
95 if (OrthancPlugins::RestApiPost(response, "/tools/find", request, false) &&
96 response.type() == Json::arrayValue)
97 {
98 for (Json::Value::ArrayIndex i = 0; i < response.size(); i++)
99 {
100 if (response[i].type() != Json::stringValue)
101 {
102 throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError);
103 }
104 else
105 {
106 publicIds.insert(response[i].asString());
107 }
108 }
109 }
110 else
111 {
112 throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError);
113 }
114 }
115 }
116 }
117
118
119 void MoveRequestHandler::LookupIdentifiers(std::set<std::string>& publicIds,
120 Orthanc::ResourceType level,
121 const Orthanc::DicomMap& input) const
122 {
123 std::string value;
124
125 switch (level)
126 {
127 case Orthanc::ResourceType_Patient:
128 if (input.LookupStringValue(value, Orthanc::DICOM_TAG_PATIENT_ID, false) &&
129 !value.empty())
130 {
131 ExecuteLookup(publicIds, level, Orthanc::DICOM_TAG_PATIENT_ID, value);
132 }
133 break;
134
135 case Orthanc::ResourceType_Study:
136 if (input.LookupStringValue(value, Orthanc::DICOM_TAG_STUDY_INSTANCE_UID, false) &&
137 !value.empty())
138 {
139 ExecuteLookup(publicIds, level, Orthanc::DICOM_TAG_STUDY_INSTANCE_UID, value);
140 }
141 else if (input.LookupStringValue(value, Orthanc::DICOM_TAG_ACCESSION_NUMBER, false) &&
142 !value.empty())
143 {
144 ExecuteLookup(publicIds, level, Orthanc::DICOM_TAG_ACCESSION_NUMBER, value);
145 }
146 break;
147
148 case Orthanc::ResourceType_Series:
149 if (input.LookupStringValue(value, Orthanc::DICOM_TAG_SERIES_INSTANCE_UID, false) &&
150 !value.empty())
151 {
152 ExecuteLookup(publicIds, level, Orthanc::DICOM_TAG_SERIES_INSTANCE_UID, value);
153 }
154 break;
155
156 case Orthanc::ResourceType_Instance:
157 if (input.LookupStringValue(value, Orthanc::DICOM_TAG_SOP_INSTANCE_UID, false) &&
158 !value.empty())
159 {
160 ExecuteLookup(publicIds, level, Orthanc::DICOM_TAG_SOP_INSTANCE_UID, value);
161 }
162 break;
163
164 default:
165 throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange);
166 }
167 }
168
169
170 Orthanc::IMoveRequestIterator* MoveRequestHandler::Handle(const std::string& targetAet,
171 const Orthanc::DicomMap& input,
172 const std::string& originatorIp,
173 const std::string& originatorAet,
174 const std::string& calledAet,
175 uint16_t originatorId)
176 {
177 std::set<std::string> publicIds;
178
179 std::string s;
180 if (input.LookupStringValue(s, Orthanc::DICOM_TAG_QUERY_RETRIEVE_LEVEL, false) &&
181 !s.empty())
182 {
183 LookupIdentifiers(publicIds, PluginToolbox::ParseQueryRetrieveLevel(s), input);
184 }
185 else
186 {
187 // The query level is not present in the C-Move request, which
188 // does not follow the DICOM standard. This is for instance the
189 // behavior of Tudor DICOM. Try and automatically deduce the
190 // query level: Start from the instance level, going up to the
191 // patient level until a valid DICOM identifier is found.
192 LookupIdentifiers(publicIds, Orthanc::ResourceType_Instance, input);
193
194 if (publicIds.empty())
195 {
196 LookupIdentifiers(publicIds, Orthanc::ResourceType_Series, input);
197 }
198
199 if (publicIds.empty())
200 {
201 LookupIdentifiers(publicIds, Orthanc::ResourceType_Study, input);
202 }
203
204 if (publicIds.empty())
205 {
206 LookupIdentifiers(publicIds, Orthanc::ResourceType_Patient, input);
207 }
208 }
209
210 Json::Value resources = Json::arrayValue;
211 for (std::set<std::string>::const_iterator it = publicIds.begin(); it != publicIds.end(); ++it)
212 {
213 resources.append(*it);
214 }
215
216 std::string targetName;
217 Orthanc::RemoteModalityParameters targetParameters;
218 if (!PluginToolbox::LookupAETitle(targetName, targetParameters, isStrictAet_, targetAet))
219 {
220 throw Orthanc::OrthancException(Orthanc::ErrorCode_NetworkProtocol, "Unknown target AET: " + targetAet);
221 }
222
223 Json::Value body;
224 body["CalledAet"] = calledAet;
225 body["MoveOriginatorAet"] = originatorAet;
226 body["MoveOriginatorID"] = originatorId;
227 body["Resources"] = resources;
228 body["Synchronous"] = isSynchronous_;
229
230 return new Iterator(targetName, body);
231 }