comparison Plugin/Plugin.cpp @ 0:02f7a0400a91

initial commit
author Sebastien Jodogne <s.jodogne@gmail.com>
date Wed, 25 Feb 2015 13:45:35 +0100
parents
children ecefd45026bf
comparison
equal deleted inserted replaced
-1:000000000000 0:02f7a0400a91
1 /**
2 * Orthanc - A Lightweight, RESTful DICOM Store
3 * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
4 * Department, University Hospital of Liege, Belgium
5 *
6 * This program is free software: you can redistribute it and/or
7 * modify it under the terms of the GNU Affero General Public License
8 * as published by the Free Software Foundation, either version 3 of
9 * the License, or (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful, but
12 * WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Affero General Public License for more details.
15 *
16 * You should have received a copy of the GNU Affero General Public License
17 * along with this program. If not, see <http://www.gnu.org/licenses/>.
18 **/
19
20
21 #include <boost/thread.hpp>
22 #include <boost/lexical_cast.hpp>
23 #include <EmbeddedResources.h>
24
25 #include "../Orthanc/OrthancException.h"
26 #include "ViewerToolbox.h"
27 #include "ViewerPrefetchPolicy.h"
28 #include "DecodedImageAdapter.h"
29 #include "InstanceInformationAdapter.h"
30 #include "SeriesInformationAdapter.h"
31
32
33
34 class CacheContext
35 {
36 private:
37 std::auto_ptr<Orthanc::FilesystemStorage> storage_;
38 std::auto_ptr<Orthanc::SQLite::Connection> db_;
39 std::auto_ptr<OrthancPlugins::CacheManager> cache_;
40 std::auto_ptr<OrthancPlugins::CacheScheduler> scheduler_;
41
42 public:
43 CacheContext(const std::string& path)
44 {
45 boost::filesystem::path p(path);
46
47 storage_.reset(new Orthanc::FilesystemStorage(path));
48 db_.reset(new Orthanc::SQLite::Connection());
49 db_->Open((p / "cache.db").string());
50
51 cache_.reset(new OrthancPlugins::CacheManager(*db_, *storage_));
52 //cache_->SetSanityCheckEnabled(true); // For debug
53
54 scheduler_.reset(new OrthancPlugins::CacheScheduler(*cache_, 100));
55 }
56
57 OrthancPlugins::CacheScheduler& GetScheduler()
58 {
59 return *scheduler_;
60 }
61 };
62
63
64 static OrthancPluginContext* context_ = NULL;
65 static CacheContext* cache_ = NULL;
66
67
68
69 static int32_t OnChangeCallback(OrthancPluginChangeType changeType,
70 OrthancPluginResourceType resourceType,
71 const char* resourceId)
72 {
73 try
74 {
75 if (changeType == OrthancPluginChangeType_NewInstance &&
76 resourceType == OrthancPluginResourceType_Instance)
77 {
78 // On the reception of a new instance, precompute its spatial position
79 cache_->GetScheduler().Prefetch(OrthancPlugins::CacheBundle_InstanceInformation, resourceId);
80
81 // Indalidate the parent series of the instance
82 std::string uri = "/instances/" + std::string(resourceId);
83 Json::Value instance;
84 if (OrthancPlugins::GetJsonFromOrthanc(instance, context_, uri))
85 {
86 std::string seriesId = instance["ParentSeries"].asString();
87 cache_->GetScheduler().Invalidate(OrthancPlugins::CacheBundle_SeriesInformation, seriesId);
88 }
89 }
90
91 return 0;
92 }
93 catch (std::runtime_error& e)
94 {
95 OrthancPluginLogError(context_, e.what());
96 return 0; // Ignore error
97 }
98 }
99
100
101
102 template <enum OrthancPlugins::CacheBundle bundle>
103 int32_t ServeCache(OrthancPluginRestOutput* output,
104 const char* url,
105 const OrthancPluginHttpRequest* request)
106 {
107 try
108 {
109 if (request->method != OrthancPluginHttpMethod_Get)
110 {
111 OrthancPluginSendMethodNotAllowed(context_, output, "GET");
112 return 0;
113 }
114
115 const std::string id = request->groups[0];
116 std::string content;
117
118 if (cache_->GetScheduler().Access(content, bundle, id))
119 {
120 OrthancPluginAnswerBuffer(context_, output, content.c_str(), content.size(), "application/json");
121 }
122 else
123 {
124 OrthancPluginSendHttpStatusCode(context_, output, 404);
125 }
126
127 return 0;
128 }
129 catch (Orthanc::OrthancException& e)
130 {
131 OrthancPluginLogError(context_, e.What());
132 return -1;
133 }
134 catch (std::runtime_error& e)
135 {
136 OrthancPluginLogError(context_, e.what());
137 return -1;
138 }
139 catch (boost::bad_lexical_cast&)
140 {
141 OrthancPluginLogError(context_, "Bad lexical cast");
142 return -1;
143 }
144 }
145
146
147
148
149 #if ORTHANC_STANDALONE == 0
150 static int32_t ServeWebViewer(OrthancPluginRestOutput* output,
151 const char* url,
152 const OrthancPluginHttpRequest* request)
153 {
154 if (request->method != OrthancPluginHttpMethod_Get)
155 {
156 OrthancPluginSendMethodNotAllowed(context_, output, "GET");
157 return 0;
158 }
159
160 const std::string path = std::string(WEB_VIEWER_PATH) + std::string(request->groups[0]);
161 const char* mime = OrthancPlugins::GetMimeType(path);
162
163 std::string s;
164 if (OrthancPlugins::ReadFile(s, path))
165 {
166 const char* resource = s.size() ? s.c_str() : NULL;
167 OrthancPluginAnswerBuffer(context_, output, resource, s.size(), mime);
168 }
169 else
170 {
171 std::string s = "Inexistent file in served folder: " + path;
172 OrthancPluginLogError(context_, s.c_str());
173 OrthancPluginSendHttpStatusCode(context_, output, 404);
174 }
175
176 return 0;
177 }
178 #endif
179
180
181
182 template <enum OrthancPlugins::EmbeddedResources::DirectoryResourceId folder>
183 static int32_t ServeEmbeddedFolder(OrthancPluginRestOutput* output,
184 const char* url,
185 const OrthancPluginHttpRequest* request)
186 {
187 if (request->method != OrthancPluginHttpMethod_Get)
188 {
189 OrthancPluginSendMethodNotAllowed(context_, output, "GET");
190 return 0;
191 }
192
193 std::string path = "/" + std::string(request->groups[0]);
194 const char* mime = OrthancPlugins::GetMimeType(path);
195
196 try
197 {
198 std::string s;
199 OrthancPlugins::EmbeddedResources::GetDirectoryResource(s, folder, path.c_str());
200
201 const char* resource = s.size() ? s.c_str() : NULL;
202 OrthancPluginAnswerBuffer(context_, output, resource, s.size(), mime);
203
204 return 0;
205 }
206 catch (std::runtime_error&)
207 {
208 std::string s = "Unknown static resource in plugin: " + std::string(request->groups[0]);
209 OrthancPluginLogError(context_, s.c_str());
210 OrthancPluginSendHttpStatusCode(context_, output, 404);
211 return 0;
212 }
213 }
214
215
216
217
218 extern "C"
219 {
220 ORTHANC_PLUGINS_API int32_t OrthancPluginInitialize(OrthancPluginContext* context)
221 {
222 using namespace OrthancPlugins;
223
224 context_ = context;
225 OrthancPluginLogWarning(context_, "Initializing the Web viewer");
226
227
228 /* Check the version of the Orthanc core */
229 if (OrthancPluginCheckVersion(context_) == 0)
230 {
231 char info[1024];
232 sprintf(info, "Your version of Orthanc (%s) must be above %d.%d.%d to run this plugin",
233 context_->orthancVersion,
234 ORTHANC_PLUGINS_MINIMAL_MAJOR_NUMBER,
235 ORTHANC_PLUGINS_MINIMAL_MINOR_NUMBER,
236 ORTHANC_PLUGINS_MINIMAL_REVISION_NUMBER);
237 OrthancPluginLogError(context_, info);
238 return -1;
239 }
240
241 OrthancPluginSetDescription(context_, "Provides a Web viewer of DICOM series within Orthanc.");
242
243
244 /* By default, use half of the available processing cores for the decoding of DICOM images */
245 int decodingThreads = boost::thread::hardware_concurrency() / 2;
246 if (decodingThreads == 0)
247 {
248 decodingThreads = 1;
249 }
250
251
252 try
253 {
254 /* Read the configuration of the Web viewer */
255 Json::Value configuration;
256 if (!ReadConfiguration(configuration, context))
257 {
258 OrthancPluginLogError(context_, "Unable to read the configuration file of Orthanc");
259 return -1;
260 }
261
262 std::string cachePath = "WebViewerCache";
263
264 if (configuration.isMember("WebViewer"))
265 {
266 cachePath = GetStringValue(configuration["WebViewer"], "Cache", cachePath);
267 decodingThreads = GetIntegerValue(configuration["WebViewer"], "Threads", decodingThreads);
268 }
269
270 std::string message = ("Web viewer using " + boost::lexical_cast<std::string>(decodingThreads) +
271 " threads for the decoding of the DICOM images");
272 OrthancPluginLogWarning(context_, message.c_str());
273
274 if (decodingThreads <= 0)
275 {
276 throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange);
277 }
278
279 message = "Storing the cache of the Web viewer in folder: " + cachePath;
280 OrthancPluginLogWarning(context_, message.c_str());
281
282
283 /* Create the cache */
284 cache_ = new CacheContext(cachePath);
285 cache_->GetScheduler().RegisterPolicy(new ViewerPrefetchPolicy(context_));
286 cache_->GetScheduler().Register(CacheBundle_SeriesInformation,
287 new SeriesInformationAdapter(context_, cache_->GetScheduler()), 1);
288 cache_->GetScheduler().Register(CacheBundle_InstanceInformation,
289 new InstanceInformationAdapter(context_), 1);
290 cache_->GetScheduler().Register(CacheBundle_DecodedImage,
291 new DecodedImageAdapter(context_), decodingThreads);
292 }
293 catch (std::runtime_error& e)
294 {
295 OrthancPluginLogError(context_, e.what());
296 return -1;
297 }
298 catch (Orthanc::OrthancException& e)
299 {
300 OrthancPluginLogError(context_, e.What());
301 return -1;
302 }
303
304
305 /* Install the callbacks */
306 OrthancPluginRegisterRestCallback(context_, "/web-viewer/series/(.*)", ServeCache<CacheBundle_SeriesInformation>);
307 OrthancPluginRegisterRestCallback(context_, "/web-viewer/instances/(.*)", ServeCache<CacheBundle_DecodedImage>);
308 OrthancPluginRegisterRestCallback(context, "/web-viewer/libs/(.*)", ServeEmbeddedFolder<EmbeddedResources::JAVASCRIPT_LIBS>);
309
310 #if ORTHANC_STANDALONE == 1
311 OrthancPluginRegisterRestCallback(context, "/web-viewer/app/(.*)", ServeEmbeddedFolder<EmbeddedResources::WEB_VIEWER>);
312 #else
313 OrthancPluginRegisterRestCallback(context, "/web-viewer/app/(.*)", ServeWebViewer);
314 #endif
315
316 OrthancPluginRegisterOnChangeCallback(context, OnChangeCallback);
317
318
319 /* Extend the default Orthanc Explorer with custom JavaScript */
320 std::string explorer;
321 EmbeddedResources::GetFileResource(explorer, EmbeddedResources::ORTHANC_EXPLORER);
322 OrthancPluginExtendOrthancExplorer(context_, explorer.c_str());
323
324 return 0;
325 }
326
327
328 ORTHANC_PLUGINS_API void OrthancPluginFinalize()
329 {
330 OrthancPluginLogWarning(context_, "Finalizing the Web viewer");
331
332 if (cache_ != NULL)
333 {
334 delete cache_;
335 cache_ = NULL;
336 }
337 }
338
339
340 ORTHANC_PLUGINS_API const char* OrthancPluginGetName()
341 {
342 return "web-viewer";
343 }
344
345
346 ORTHANC_PLUGINS_API const char* OrthancPluginGetVersion()
347 {
348 return "1.0";
349 }
350 }