Mercurial > hg > orthanc
comparison OrthancServer/Plugins/Samples/ModalityWorklists/Plugin.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 | Plugins/Samples/ModalityWorklists/Plugin.cpp@6110a4995ace |
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 * This program is distributed in the hope that it will be useful, but | |
13 * WITHOUT ANY WARRANTY; without even the implied warranty of | |
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
15 * General Public License for more details. | |
16 * | |
17 * You should have received a copy of the GNU General Public License | |
18 * along with this program. If not, see <http://www.gnu.org/licenses/>. | |
19 **/ | |
20 | |
21 | |
22 #include "../../../Core/Compatibility.h" | |
23 #include "../Common/OrthancPluginCppWrapper.h" | |
24 | |
25 #include <boost/filesystem.hpp> | |
26 #include <json/value.h> | |
27 #include <json/reader.h> | |
28 #include <string.h> | |
29 #include <iostream> | |
30 #include <algorithm> | |
31 | |
32 static std::string folder_; | |
33 static bool filterIssuerAet_ = false; | |
34 | |
35 /** | |
36 * This is the main function for matching a DICOM worklist against a query. | |
37 **/ | |
38 static bool MatchWorklist(OrthancPluginWorklistAnswers* answers, | |
39 const OrthancPluginWorklistQuery* query, | |
40 const OrthancPlugins::FindMatcher& matcher, | |
41 const std::string& path) | |
42 { | |
43 OrthancPlugins::MemoryBuffer dicom; | |
44 dicom.ReadFile(path); | |
45 | |
46 if (matcher.IsMatch(dicom)) | |
47 { | |
48 // This DICOM file matches the worklist query, add it to the answers | |
49 OrthancPluginErrorCode code = OrthancPluginWorklistAddAnswer | |
50 (OrthancPlugins::GetGlobalContext(), answers, query, dicom.GetData(), dicom.GetSize()); | |
51 | |
52 if (code != OrthancPluginErrorCode_Success) | |
53 { | |
54 OrthancPlugins::LogError("Error while adding an answer to a worklist request"); | |
55 ORTHANC_PLUGINS_THROW_PLUGIN_ERROR_CODE(code); | |
56 } | |
57 | |
58 return true; | |
59 } | |
60 | |
61 return false; | |
62 } | |
63 | |
64 | |
65 static OrthancPlugins::FindMatcher* CreateMatcher(const OrthancPluginWorklistQuery* query, | |
66 const char* issuerAet) | |
67 { | |
68 // Extract the DICOM instance underlying the C-Find query | |
69 OrthancPlugins::MemoryBuffer dicom; | |
70 dicom.GetDicomQuery(query); | |
71 | |
72 // Convert the DICOM as JSON, and dump it to the user in "--verbose" mode | |
73 Json::Value json; | |
74 dicom.DicomToJson(json, OrthancPluginDicomToJsonFormat_Short, | |
75 static_cast<OrthancPluginDicomToJsonFlags>(0), 0); | |
76 | |
77 OrthancPlugins::LogInfo("Received worklist query from remote modality " + | |
78 std::string(issuerAet) + ":\n" + json.toStyledString()); | |
79 | |
80 if (!filterIssuerAet_) | |
81 { | |
82 return new OrthancPlugins::FindMatcher(query); | |
83 } | |
84 else | |
85 { | |
86 // Alternative sample showing how to fine-tune an incoming C-Find | |
87 // request, before matching it against the worklist database. The | |
88 // code below will restrict the original DICOM request by | |
89 // requesting the ScheduledStationAETitle to correspond to the AET | |
90 // of the C-Find issuer. This code will make the integration test | |
91 // "test_filter_issuer_aet" succeed (cf. the orthanc-tests repository). | |
92 | |
93 static const char* SCHEDULED_PROCEDURE_STEP_SEQUENCE = "0040,0100"; | |
94 static const char* SCHEDULED_STATION_AETITLE = "0040,0001"; | |
95 static const char* PREGNANCY_STATUS = "0010,21c0"; | |
96 | |
97 if (!json.isMember(SCHEDULED_PROCEDURE_STEP_SEQUENCE)) | |
98 { | |
99 // Create a ScheduledProcedureStepSequence sequence, with one empty element | |
100 json[SCHEDULED_PROCEDURE_STEP_SEQUENCE] = Json::arrayValue; | |
101 json[SCHEDULED_PROCEDURE_STEP_SEQUENCE].append(Json::objectValue); | |
102 } | |
103 | |
104 Json::Value& v = json[SCHEDULED_PROCEDURE_STEP_SEQUENCE]; | |
105 | |
106 if (v.type() != Json::arrayValue || | |
107 v.size() != 1 || | |
108 v[0].type() != Json::objectValue) | |
109 { | |
110 ORTHANC_PLUGINS_THROW_EXCEPTION(BadFileFormat); | |
111 } | |
112 | |
113 // Set the ScheduledStationAETitle if none was provided | |
114 if (!v[0].isMember(SCHEDULED_STATION_AETITLE) || | |
115 v[0].type() != Json::stringValue || | |
116 v[0][SCHEDULED_STATION_AETITLE].asString().size() == 0 || | |
117 v[0][SCHEDULED_STATION_AETITLE].asString() == "*") | |
118 { | |
119 v[0][SCHEDULED_STATION_AETITLE] = issuerAet; | |
120 } | |
121 | |
122 if (json.isMember(PREGNANCY_STATUS) && | |
123 json[PREGNANCY_STATUS].asString().size() == 0) | |
124 { | |
125 json.removeMember(PREGNANCY_STATUS); | |
126 } | |
127 | |
128 // Encode the modified JSON as a DICOM instance, then convert it to a C-Find matcher | |
129 OrthancPlugins::MemoryBuffer modified; | |
130 modified.CreateDicom(json, OrthancPluginCreateDicomFlags_None); | |
131 | |
132 return new OrthancPlugins::FindMatcher(modified); | |
133 } | |
134 } | |
135 | |
136 | |
137 | |
138 OrthancPluginErrorCode Callback(OrthancPluginWorklistAnswers* answers, | |
139 const OrthancPluginWorklistQuery* query, | |
140 const char* issuerAet, | |
141 const char* calledAet) | |
142 { | |
143 try | |
144 { | |
145 // Construct an object to match the worklists in the database against the C-Find query | |
146 std::unique_ptr<OrthancPlugins::FindMatcher> matcher(CreateMatcher(query, issuerAet)); | |
147 | |
148 // Loop over the regular files in the database folder | |
149 namespace fs = boost::filesystem; | |
150 | |
151 fs::path source(folder_); | |
152 fs::directory_iterator end; | |
153 int parsedFilesCount = 0; | |
154 int matchedWorklistCount = 0; | |
155 | |
156 try | |
157 { | |
158 for (fs::directory_iterator it(source); it != end; ++it) | |
159 { | |
160 fs::file_type type(it->status().type()); | |
161 | |
162 if (type == fs::regular_file || | |
163 type == fs::reparse_file) // cf. BitBucket issue #11 | |
164 { | |
165 std::string extension = fs::extension(it->path()); | |
166 std::transform(extension.begin(), extension.end(), extension.begin(), tolower); // Convert to lowercase | |
167 | |
168 if (extension == ".wl") | |
169 { | |
170 parsedFilesCount++; | |
171 // We found a worklist (i.e. a DICOM find with extension ".wl"), match it against the query | |
172 if (MatchWorklist(answers, query, *matcher, it->path().string())) | |
173 { | |
174 OrthancPlugins::LogInfo("Worklist matched: " + it->path().string()); | |
175 matchedWorklistCount++; | |
176 } | |
177 } | |
178 } | |
179 } | |
180 | |
181 std::ostringstream message; | |
182 message << "Worklist C-Find: parsed " << parsedFilesCount << " files, found " << matchedWorklistCount << " match(es)"; | |
183 OrthancPlugins::LogInfo(message.str()); | |
184 | |
185 } | |
186 catch (fs::filesystem_error&) | |
187 { | |
188 OrthancPlugins::LogError("Inexistent folder while scanning for worklists: " + source.string()); | |
189 return OrthancPluginErrorCode_DirectoryExpected; | |
190 } | |
191 | |
192 // Uncomment the following line if too many answers are to be returned | |
193 // OrthancPluginMarkWorklistAnswersIncomplete(OrthancPlugins::GetGlobalContext(), answers); | |
194 | |
195 return OrthancPluginErrorCode_Success; | |
196 } | |
197 catch (OrthancPlugins::PluginException& e) | |
198 { | |
199 return e.GetErrorCode(); | |
200 } | |
201 } | |
202 | |
203 | |
204 extern "C" | |
205 { | |
206 ORTHANC_PLUGINS_API int32_t OrthancPluginInitialize(OrthancPluginContext* c) | |
207 { | |
208 OrthancPlugins::SetGlobalContext(c); | |
209 | |
210 /* Check the version of the Orthanc core */ | |
211 if (OrthancPluginCheckVersion(c) == 0) | |
212 { | |
213 OrthancPlugins::ReportMinimalOrthancVersion(ORTHANC_PLUGINS_MINIMAL_MAJOR_NUMBER, | |
214 ORTHANC_PLUGINS_MINIMAL_MINOR_NUMBER, | |
215 ORTHANC_PLUGINS_MINIMAL_REVISION_NUMBER); | |
216 return -1; | |
217 } | |
218 | |
219 OrthancPlugins::LogWarning("Sample worklist plugin is initializing"); | |
220 OrthancPluginSetDescription(c, "Serve DICOM modality worklists from a folder with Orthanc."); | |
221 | |
222 OrthancPlugins::OrthancConfiguration configuration; | |
223 | |
224 OrthancPlugins::OrthancConfiguration worklists; | |
225 configuration.GetSection(worklists, "Worklists"); | |
226 | |
227 bool enabled = worklists.GetBooleanValue("Enable", false); | |
228 if (enabled) | |
229 { | |
230 if (worklists.LookupStringValue(folder_, "Database")) | |
231 { | |
232 OrthancPlugins::LogWarning("The database of worklists will be read from folder: " + folder_); | |
233 OrthancPluginRegisterWorklistCallback(OrthancPlugins::GetGlobalContext(), Callback); | |
234 } | |
235 else | |
236 { | |
237 OrthancPlugins::LogError("The configuration option \"Worklists.Database\" must contain a path"); | |
238 return -1; | |
239 } | |
240 | |
241 filterIssuerAet_ = worklists.GetBooleanValue("FilterIssuerAet", false); | |
242 } | |
243 else | |
244 { | |
245 OrthancPlugins::LogWarning("Worklist server is disabled by the configuration file"); | |
246 } | |
247 | |
248 return 0; | |
249 } | |
250 | |
251 | |
252 ORTHANC_PLUGINS_API void OrthancPluginFinalize() | |
253 { | |
254 OrthancPlugins::LogWarning("Sample worklist plugin is finalizing"); | |
255 } | |
256 | |
257 | |
258 ORTHANC_PLUGINS_API const char* OrthancPluginGetName() | |
259 { | |
260 return "worklists"; | |
261 } | |
262 | |
263 | |
264 ORTHANC_PLUGINS_API const char* OrthancPluginGetVersion() | |
265 { | |
266 return MODALITY_WORKLISTS_VERSION; | |
267 } | |
268 } |