Mercurial > hg > orthanc-indexer
annotate Sources/Plugin.cpp @ 21:1185a9f8c6c9
fix deprecated calls
author | Sebastien Jodogne <s.jodogne@gmail.com> |
---|---|
date | Fri, 22 Mar 2024 15:20:22 +0100 |
parents | ca40236fda57 |
children |
rev | line source |
---|---|
0 | 1 /** |
2 * Indexer plugin for Orthanc | |
20 | 3 * Copyright (C) 2021-2024 Sebastien Jodogne, UCLouvain, Belgium |
0 | 4 * |
5 * This program is free software: you can redistribute it and/or | |
6 * modify it under the terms of the GNU General Public License as | |
7 * published by the Free Software Foundation, either version 3 of the | |
8 * License, or (at your option) any later version. | |
9 * | |
10 * This program is distributed in the hope that it will be useful, but | |
11 * WITHOUT ANY WARRANTY; without even the implied warranty of | |
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
13 * General Public License for more details. | |
14 * | |
15 * You should have received a copy of the GNU General Public License | |
16 * along with this program. If not, see <http://www.gnu.org/licenses/>. | |
17 **/ | |
18 | |
19 | |
20 #include "IndexerDatabase.h" | |
21 #include "StorageArea.h" | |
22 | |
23 #include "../Resources/Orthanc/Plugins/OrthancPluginCppWrapper.h" | |
24 | |
25 #include <DicomFormat/DicomInstanceHasher.h> | |
26 #include <DicomFormat/DicomMap.h> | |
27 #include <Logging.h> | |
28 #include <SerializationToolbox.h> | |
29 #include <SystemToolbox.h> | |
30 | |
31 #include <boost/filesystem.hpp> | |
32 #include <boost/thread.hpp> | |
3
e731f308b8b1
added missing include
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
1
diff
changeset
|
33 #include <stack> |
0 | 34 |
21
1185a9f8c6c9
fix deprecated calls
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
20
diff
changeset
|
35 #define ORTHANC_PLUGIN_NAME "indexer" |
1185a9f8c6c9
fix deprecated calls
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
20
diff
changeset
|
36 |
0 | 37 |
38 static std::list<std::string> folders_; | |
39 static IndexerDatabase database_; | |
40 static std::unique_ptr<StorageArea> storageArea_; | |
7
03157465f6f4
added "Interval" configuration option
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
6
diff
changeset
|
41 static unsigned int intervalSeconds_; |
0 | 42 |
43 | |
44 static bool ComputeInstanceId(std::string& instanceId, | |
45 const void* dicom, | |
46 size_t size) | |
47 { | |
48 if (size > 0 && | |
49 Orthanc::DicomMap::IsDicomFile(dicom, size)) | |
50 { | |
51 try | |
52 { | |
53 OrthancPlugins::OrthancString s; | |
54 s.Assign(OrthancPluginDicomBufferToJson(OrthancPlugins::GetGlobalContext(), dicom, size, | |
55 OrthancPluginDicomToJsonFormat_Short, | |
56 OrthancPluginDicomToJsonFlags_None, 256)); | |
57 | |
58 Json::Value json; | |
59 s.ToJson(json); | |
60 | |
61 static const char* const PATIENT_ID = "0010,0020"; | |
62 static const char* const STUDY_INSTANCE_UID = "0020,000d"; | |
63 static const char* const SERIES_INSTANCE_UID = "0020,000e"; | |
64 static const char* const SOP_INSTANCE_UID = "0008,0018"; | |
65 | |
66 Orthanc::DicomInstanceHasher hasher( | |
67 json.isMember(PATIENT_ID) ? Orthanc::SerializationToolbox::ReadString(json, PATIENT_ID) : "", | |
68 Orthanc::SerializationToolbox::ReadString(json, STUDY_INSTANCE_UID), | |
69 Orthanc::SerializationToolbox::ReadString(json, SERIES_INSTANCE_UID), | |
70 Orthanc::SerializationToolbox::ReadString(json, SOP_INSTANCE_UID)); | |
71 | |
72 instanceId = hasher.HashInstance(); | |
73 return true; | |
74 } | |
75 catch (Orthanc::OrthancException&) | |
76 { | |
77 return false; | |
78 } | |
79 } | |
80 else | |
81 { | |
82 return false; | |
83 } | |
84 } | |
85 | |
86 | |
87 | |
88 static void ProcessFile(const std::string& path, | |
89 const std::time_t time, | |
90 const uintmax_t size) | |
91 { | |
92 std::string oldInstanceId; | |
93 IndexerDatabase::FileStatus status = database_.LookupFile(oldInstanceId, path, time, size); | |
94 | |
95 if (status == IndexerDatabase::FileStatus_New || | |
96 status == IndexerDatabase::FileStatus_Modified) | |
97 { | |
98 if (status == IndexerDatabase::FileStatus_Modified) | |
99 { | |
100 database_.RemoveFile(path); | |
101 } | |
102 | |
103 std::string dicom; | |
104 Orthanc::SystemToolbox::ReadFile(dicom, path); | |
105 | |
106 std::string instanceId; | |
107 if (!dicom.empty() && | |
108 ComputeInstanceId(instanceId, dicom.c_str(), dicom.size())) | |
109 { | |
110 LOG(INFO) << "New DICOM file detected by the indexer plugin: " << path; | |
111 | |
112 // The following line must be *before* the "RestApiDelete()" to | |
113 // deal with the case of having two copies of the same DICOM | |
114 // file in the indexed folders, but with different timestamps | |
115 database_.AddDicomInstance(path, time, size, instanceId); | |
116 | |
117 if (status == IndexerDatabase::FileStatus_Modified) | |
118 { | |
119 OrthancPlugins::RestApiDelete("/instances/" + oldInstanceId, false); | |
120 } | |
121 | |
122 try | |
123 { | |
124 Json::Value upload; | |
125 OrthancPlugins::RestApiPost(upload, "/instances", dicom, false); | |
126 } | |
127 catch (Orthanc::OrthancException&) | |
128 { | |
129 } | |
130 } | |
131 else | |
132 { | |
133 LOG(INFO) << "Skipping indexing of non-DICOM file: " << path; | |
134 database_.AddNonDicomFile(path, time, size); | |
135 | |
136 if (status == IndexerDatabase::FileStatus_Modified) | |
137 { | |
138 OrthancPlugins::RestApiDelete("/instances/" + oldInstanceId, false); | |
139 } | |
140 } | |
141 } | |
142 } | |
143 | |
144 | |
145 static void LookupDeletedFiles() | |
146 { | |
147 class Visitor : public IndexerDatabase::IFileVisitor | |
148 { | |
149 private: | |
150 typedef std::pair<std::string, std::string> DeletedDicom; | |
151 | |
152 std::list<DeletedDicom> deletedDicom_; | |
153 | |
154 public: | |
155 virtual void VisitInstance(const std::string& path, | |
156 bool isDicom, | |
157 const std::string& instanceId) ORTHANC_OVERRIDE | |
158 { | |
159 if (!Orthanc::SystemToolbox::IsRegularFile(path) && | |
160 isDicom) | |
161 { | |
162 deletedDicom_.push_back(std::make_pair(path, instanceId)); | |
163 } | |
164 } | |
165 | |
166 void ExecuteDelete() | |
167 { | |
168 for (std::list<DeletedDicom>::const_iterator | |
169 it = deletedDicom_.begin(); it != deletedDicom_.end(); ++it) | |
170 { | |
171 const std::string& path = it->first; | |
172 const std::string& instanceId = it->second; | |
173 | |
174 if (database_.RemoveFile(path)) | |
175 { | |
176 OrthancPlugins::RestApiDelete("/instances/" + instanceId, false); | |
177 } | |
178 } | |
179 } | |
180 }; | |
181 | |
182 Visitor visitor; | |
183 database_.Apply(visitor); | |
184 visitor.ExecuteDelete(); | |
185 } | |
186 | |
187 | |
7
03157465f6f4
added "Interval" configuration option
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
6
diff
changeset
|
188 static void MonitorDirectories(bool* stop, unsigned int intervalSeconds) |
0 | 189 { |
190 for (;;) | |
191 { | |
192 std::stack<boost::filesystem::path> s; | |
193 | |
194 for (std::list<std::string>::const_iterator it = folders_.begin(); | |
195 it != folders_.end(); ++it) | |
196 { | |
197 s.push(*it); | |
198 } | |
199 | |
200 while (!s.empty()) | |
201 { | |
202 if (*stop) | |
203 { | |
204 return; | |
205 } | |
206 | |
207 boost::filesystem::path d = s.top(); | |
208 s.pop(); | |
209 | |
210 boost::filesystem::directory_iterator current; | |
211 | |
212 try | |
213 { | |
214 current = boost::filesystem::directory_iterator(d); | |
215 } | |
216 catch (boost::filesystem::filesystem_error&) | |
217 { | |
12 | 218 LOG(WARNING) << "Indexer plugin cannot read directory: " << d.string(); |
219 continue; | |
0 | 220 } |
221 | |
222 const boost::filesystem::directory_iterator end; | |
223 | |
224 while (current != end) | |
225 { | |
226 try | |
227 { | |
228 const boost::filesystem::file_status status = boost::filesystem::status(current->path()); | |
229 | |
230 switch (status.type()) | |
231 { | |
232 case boost::filesystem::regular_file: | |
233 case boost::filesystem::reparse_file: | |
234 try | |
235 { | |
236 ProcessFile(current->path().string(), | |
237 boost::filesystem::last_write_time(current->path()), | |
238 boost::filesystem::file_size(current->path())); | |
239 } | |
240 catch (Orthanc::OrthancException& e) | |
241 { | |
242 LOG(ERROR) << e.What(); | |
243 } | |
244 break; | |
245 | |
246 case boost::filesystem::directory_file: | |
247 s.push(current->path()); | |
248 break; | |
249 | |
250 default: | |
251 break; | |
252 } | |
253 } | |
254 catch (boost::filesystem::filesystem_error&) | |
255 { | |
256 } | |
257 | |
258 ++current; | |
259 } | |
260 } | |
261 | |
262 try | |
263 { | |
264 LookupDeletedFiles(); | |
265 } | |
266 catch (Orthanc::OrthancException& e) | |
267 { | |
268 LOG(ERROR) << e.What(); | |
269 } | |
270 | |
7
03157465f6f4
added "Interval" configuration option
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
6
diff
changeset
|
271 for (unsigned int i = 0; i < intervalSeconds * 10; i++) |
0 | 272 { |
273 if (*stop) | |
274 { | |
275 return; | |
276 } | |
277 | |
278 boost::this_thread::sleep(boost::posix_time::milliseconds(100)); | |
279 } | |
280 } | |
281 } | |
282 | |
283 | |
284 static OrthancPluginErrorCode StorageCreate(const char *uuid, | |
285 const void *content, | |
286 int64_t size, | |
287 OrthancPluginContentType type) | |
288 { | |
289 try | |
290 { | |
291 std::string instanceId; | |
292 if (type == OrthancPluginContentType_Dicom && | |
1
d745ea3db32c
fix import of external DICOM
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
0
diff
changeset
|
293 ComputeInstanceId(instanceId, content, size) && |
d745ea3db32c
fix import of external DICOM
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
0
diff
changeset
|
294 database_.AddAttachment(uuid, instanceId)) |
0 | 295 { |
296 // This attachment corresponds to an external DICOM file that is | |
297 // stored in one of the indexed folders, only store a link to it | |
298 } | |
299 else | |
300 { | |
301 // This attachment must be stored in the internal storage area | |
302 storageArea_->Create(uuid, content, size); | |
303 } | |
304 | |
305 return OrthancPluginErrorCode_Success; | |
306 } | |
307 catch (Orthanc::OrthancException& e) | |
308 { | |
309 LOG(ERROR) << e.What(); | |
310 return static_cast<OrthancPluginErrorCode>(e.GetErrorCode()); | |
311 } | |
312 catch (...) | |
313 { | |
314 return OrthancPluginErrorCode_InternalError; | |
315 } | |
316 } | |
317 | |
318 | |
319 | |
320 static bool LookupExternalDicom(std::string& externalPath, | |
321 const char *uuid, | |
322 OrthancPluginContentType type) | |
323 { | |
324 return (type == OrthancPluginContentType_Dicom && | |
325 database_.LookupAttachment(externalPath, uuid)); | |
326 } | |
327 | |
328 | |
329 static OrthancPluginErrorCode StorageReadRange(OrthancPluginMemoryBuffer64 *target, | |
330 const char *uuid, | |
331 OrthancPluginContentType type, | |
332 uint64_t rangeStart) | |
333 { | |
334 try | |
335 { | |
336 std::string externalPath; | |
337 if (LookupExternalDicom(externalPath, uuid, type)) | |
338 { | |
339 StorageArea::ReadRangeFromPath(target, externalPath, rangeStart); | |
340 } | |
341 else | |
342 { | |
343 storageArea_->ReadRange(target, uuid, rangeStart); | |
344 } | |
345 | |
346 return OrthancPluginErrorCode_Success; | |
347 } | |
348 catch (Orthanc::OrthancException& e) | |
349 { | |
350 LOG(ERROR) << e.What(); | |
351 return static_cast<OrthancPluginErrorCode>(e.GetErrorCode()); | |
352 } | |
353 catch (...) | |
354 { | |
355 return OrthancPluginErrorCode_InternalError; | |
356 } | |
357 } | |
358 | |
359 | |
360 static OrthancPluginErrorCode StorageReadWhole(OrthancPluginMemoryBuffer64 *target, | |
361 const char *uuid, | |
362 OrthancPluginContentType type) | |
363 { | |
364 try | |
365 { | |
366 std::string externalPath; | |
367 if (LookupExternalDicom(externalPath, uuid, type)) | |
368 { | |
369 StorageArea::ReadWholeFromPath(target, externalPath); | |
370 } | |
371 else | |
372 { | |
373 storageArea_->ReadWhole(target, uuid); | |
374 } | |
375 | |
376 return OrthancPluginErrorCode_Success; | |
377 } | |
378 catch (Orthanc::OrthancException& e) | |
379 { | |
380 LOG(ERROR) << e.What(); | |
381 return static_cast<OrthancPluginErrorCode>(e.GetErrorCode()); | |
382 } | |
383 catch (...) | |
384 { | |
385 return OrthancPluginErrorCode_InternalError; | |
386 } | |
387 } | |
388 | |
389 | |
390 static OrthancPluginErrorCode StorageRemove(const char *uuid, | |
391 OrthancPluginContentType type) | |
392 { | |
393 try | |
394 { | |
395 std::string externalPath; | |
396 if (LookupExternalDicom(externalPath, uuid, type)) | |
397 { | |
398 database_.RemoveAttachment(uuid); | |
399 } | |
400 else | |
401 { | |
8 | 402 database_.RemoveAttachment(uuid); |
0 | 403 storageArea_->RemoveAttachment(uuid); |
404 } | |
405 | |
406 return OrthancPluginErrorCode_Success; | |
407 } | |
408 catch (Orthanc::OrthancException& e) | |
409 { | |
410 LOG(ERROR) << e.What(); | |
411 return static_cast<OrthancPluginErrorCode>(e.GetErrorCode()); | |
412 } | |
413 catch (...) | |
414 { | |
415 return OrthancPluginErrorCode_InternalError; | |
416 } | |
417 } | |
418 | |
419 | |
420 static OrthancPluginErrorCode OnChangeCallback(OrthancPluginChangeType changeType, | |
421 OrthancPluginResourceType resourceType, | |
422 const char* resourceId) | |
423 { | |
424 static bool stop_; | |
425 static boost::thread thread_; | |
426 | |
427 switch (changeType) | |
428 { | |
429 case OrthancPluginChangeType_OrthancStarted: | |
430 stop_ = false; | |
7
03157465f6f4
added "Interval" configuration option
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
6
diff
changeset
|
431 thread_ = boost::thread(MonitorDirectories, &stop_, intervalSeconds_); |
0 | 432 break; |
433 | |
434 case OrthancPluginChangeType_OrthancStopped: | |
435 stop_ = true; | |
436 if (thread_.joinable()) | |
437 { | |
438 thread_.join(); | |
439 } | |
440 | |
441 break; | |
442 | |
443 default: | |
444 break; | |
445 } | |
446 | |
447 return OrthancPluginErrorCode_Success; | |
448 } | |
449 | |
450 | |
451 extern "C" | |
452 { | |
453 ORTHANC_PLUGINS_API int32_t OrthancPluginInitialize(OrthancPluginContext* context) | |
454 { | |
455 OrthancPlugins::SetGlobalContext(context); | |
456 Orthanc::Logging::InitializePluginContext(context); | |
457 Orthanc::Logging::EnableInfoLevel(true); | |
458 | |
459 /* Check the version of the Orthanc core */ | |
460 if (OrthancPluginCheckVersion(context) == 0) | |
461 { | |
462 OrthancPlugins::ReportMinimalOrthancVersion(ORTHANC_PLUGINS_MINIMAL_MAJOR_NUMBER, | |
463 ORTHANC_PLUGINS_MINIMAL_MINOR_NUMBER, | |
464 ORTHANC_PLUGINS_MINIMAL_REVISION_NUMBER); | |
465 return -1; | |
466 } | |
467 | |
21
1185a9f8c6c9
fix deprecated calls
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
20
diff
changeset
|
468 OrthancPlugins::SetDescription(ORTHANC_PLUGIN_NAME, "Synchronize Orthanc with directories containing DICOM files."); |
0 | 469 |
470 OrthancPlugins::OrthancConfiguration configuration; | |
471 | |
472 OrthancPlugins::OrthancConfiguration indexer; | |
473 configuration.GetSection(indexer, "Indexer"); | |
474 | |
475 bool enabled = indexer.GetBooleanValue("Enable", false); | |
476 if (enabled) | |
477 { | |
478 try | |
479 { | |
4
da409b29cc02
by default, put the indexer database next to the orthanc database
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
3
diff
changeset
|
480 static const char* const DATABASE = "Database"; |
0 | 481 static const char* const FOLDERS = "Folders"; |
4
da409b29cc02
by default, put the indexer database next to the orthanc database
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
3
diff
changeset
|
482 static const char* const INDEX_DIRECTORY = "IndexDirectory"; |
da409b29cc02
by default, put the indexer database next to the orthanc database
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
3
diff
changeset
|
483 static const char* const ORTHANC_STORAGE = "OrthancStorage"; |
da409b29cc02
by default, put the indexer database next to the orthanc database
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
3
diff
changeset
|
484 static const char* const STORAGE_DIRECTORY = "StorageDirectory"; |
7
03157465f6f4
added "Interval" configuration option
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
6
diff
changeset
|
485 static const char* const INTERVAL = "Interval"; |
0 | 486 |
7
03157465f6f4
added "Interval" configuration option
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
6
diff
changeset
|
487 intervalSeconds_ = indexer.GetUnsignedIntegerValue(INTERVAL, 10 /* 10 seconds by default */); |
03157465f6f4
added "Interval" configuration option
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
6
diff
changeset
|
488 |
0 | 489 if (!indexer.LookupListOfStrings(folders_, FOLDERS, true) || |
490 folders_.empty()) | |
491 { | |
492 throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange, | |
493 "Missing configuration option for Indexer plugin: " + std::string(FOLDERS)); | |
494 } | |
495 | |
12 | 496 for (std::list<std::string>::const_iterator it = folders_.begin(); |
497 it != folders_.end(); ++it) | |
498 { | |
499 LOG(WARNING) << "The Indexer plugin will monitor the content of folder: " << *it; | |
500 } | |
501 | |
4
da409b29cc02
by default, put the indexer database next to the orthanc database
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
3
diff
changeset
|
502 std::string path; |
da409b29cc02
by default, put the indexer database next to the orthanc database
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
3
diff
changeset
|
503 if (!indexer.LookupStringValue(path, DATABASE)) |
da409b29cc02
by default, put the indexer database next to the orthanc database
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
3
diff
changeset
|
504 { |
da409b29cc02
by default, put the indexer database next to the orthanc database
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
3
diff
changeset
|
505 std::string folder; |
da409b29cc02
by default, put the indexer database next to the orthanc database
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
3
diff
changeset
|
506 if (!configuration.LookupStringValue(folder, INDEX_DIRECTORY)) |
da409b29cc02
by default, put the indexer database next to the orthanc database
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
3
diff
changeset
|
507 { |
da409b29cc02
by default, put the indexer database next to the orthanc database
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
3
diff
changeset
|
508 folder = configuration.GetStringValue(STORAGE_DIRECTORY, ORTHANC_STORAGE); |
da409b29cc02
by default, put the indexer database next to the orthanc database
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
3
diff
changeset
|
509 } |
da409b29cc02
by default, put the indexer database next to the orthanc database
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
3
diff
changeset
|
510 |
6
d9951c2e0bac
create parent directory of database if non-existing
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
5
diff
changeset
|
511 Orthanc::SystemToolbox::MakeDirectory(folder); |
8 | 512 path = (boost::filesystem::path(folder) / "indexer-plugin.db").string(); |
4
da409b29cc02
by default, put the indexer database next to the orthanc database
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
3
diff
changeset
|
513 } |
da409b29cc02
by default, put the indexer database next to the orthanc database
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
3
diff
changeset
|
514 |
da409b29cc02
by default, put the indexer database next to the orthanc database
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
3
diff
changeset
|
515 LOG(WARNING) << "Path to the database of the Indexer plugin: " << path; |
0 | 516 database_.Open(path); |
517 | |
4
da409b29cc02
by default, put the indexer database next to the orthanc database
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
3
diff
changeset
|
518 storageArea_.reset(new StorageArea(configuration.GetStringValue(STORAGE_DIRECTORY, ORTHANC_STORAGE))); |
0 | 519 } |
520 catch (Orthanc::OrthancException& e) | |
521 { | |
522 return -1; | |
523 } | |
524 catch (...) | |
525 { | |
526 LOG(ERROR) << "Native exception while initializing the plugin"; | |
527 return -1; | |
528 } | |
529 | |
530 OrthancPluginRegisterOnChangeCallback(context, OnChangeCallback); | |
531 OrthancPluginRegisterStorageArea2(context, StorageCreate, StorageReadWhole, StorageReadRange, StorageRemove); | |
532 } | |
533 else | |
534 { | |
535 OrthancPlugins::LogWarning("OrthancIndexer is disabled"); | |
536 } | |
537 | |
538 return 0; | |
539 } | |
540 | |
541 | |
542 ORTHANC_PLUGINS_API void OrthancPluginFinalize() | |
543 { | |
544 OrthancPlugins::LogWarning("Folder indexer plugin is finalizing"); | |
545 } | |
546 | |
547 | |
548 ORTHANC_PLUGINS_API const char* OrthancPluginGetName() | |
549 { | |
21
1185a9f8c6c9
fix deprecated calls
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
20
diff
changeset
|
550 return ORTHANC_PLUGIN_NAME; |
0 | 551 } |
552 | |
553 | |
554 ORTHANC_PLUGINS_API const char* OrthancPluginGetVersion() | |
555 { | |
556 return ORTHANC_PLUGIN_VERSION; | |
557 } | |
558 } |