Mercurial > hg > orthanc
comparison OrthancServer/Plugins/Samples/Basic/Plugin.c @ 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/Basic/Plugin.c@8aeb0c6e4628 |
children | d9473bd5ed43 |
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 <orthanc/OrthancCPlugin.h> | |
23 | |
24 #include <string.h> | |
25 #include <stdio.h> | |
26 | |
27 static OrthancPluginContext* context = NULL; | |
28 | |
29 static OrthancPluginErrorCode customError; | |
30 | |
31 | |
32 OrthancPluginErrorCode Callback1(OrthancPluginRestOutput* output, | |
33 const char* url, | |
34 const OrthancPluginHttpRequest* request) | |
35 { | |
36 char buffer[1024]; | |
37 uint32_t i; | |
38 | |
39 if (request->method != OrthancPluginHttpMethod_Get) | |
40 { | |
41 /** | |
42 * NB: Calling "OrthancPluginSendMethodNotAllowed(context, output, | |
43 * "GET");" is preferable. This is a sample to demonstrate | |
44 * "OrthancPluginSetHttpErrorDetails()". | |
45 **/ | |
46 OrthancPluginSetHttpErrorDetails(context, output, "This Callback1() can only be used by a GET call", 1 /* log */); | |
47 return OrthancPluginErrorCode_ParameterOutOfRange; | |
48 } | |
49 | |
50 sprintf(buffer, "Callback on URL [%s] with body [%s]\n", url, (const char*) request->body); | |
51 OrthancPluginLogWarning(context, buffer); | |
52 | |
53 OrthancPluginSetCookie(context, output, "hello", "world"); | |
54 OrthancPluginAnswerBuffer(context, output, buffer, strlen(buffer), "text/plain"); | |
55 | |
56 OrthancPluginLogWarning(context, ""); | |
57 | |
58 for (i = 0; i < request->groupsCount; i++) | |
59 { | |
60 sprintf(buffer, " REGEX GROUP %d = [%s]", i, request->groups[i]); | |
61 OrthancPluginLogWarning(context, buffer); | |
62 } | |
63 | |
64 OrthancPluginLogWarning(context, ""); | |
65 | |
66 for (i = 0; i < request->getCount; i++) | |
67 { | |
68 sprintf(buffer, " GET [%s] = [%s]", request->getKeys[i], request->getValues[i]); | |
69 OrthancPluginLogWarning(context, buffer); | |
70 } | |
71 | |
72 OrthancPluginLogWarning(context, ""); | |
73 | |
74 for (i = 0; i < request->headersCount; i++) | |
75 { | |
76 sprintf(buffer, " HEADERS [%s] = [%s]", request->headersKeys[i], request->headersValues[i]); | |
77 OrthancPluginLogWarning(context, buffer); | |
78 } | |
79 | |
80 OrthancPluginLogWarning(context, ""); | |
81 | |
82 return OrthancPluginErrorCode_Success; | |
83 } | |
84 | |
85 | |
86 OrthancPluginErrorCode Callback2(OrthancPluginRestOutput* output, | |
87 const char* url, | |
88 const OrthancPluginHttpRequest* request) | |
89 { | |
90 /* Answer with a sample 16bpp image. */ | |
91 | |
92 uint16_t buffer[256 * 256]; | |
93 uint32_t x, y, value; | |
94 | |
95 if (request->method != OrthancPluginHttpMethod_Get) | |
96 { | |
97 OrthancPluginSendMethodNotAllowed(context, output, "GET"); | |
98 } | |
99 else | |
100 { | |
101 value = 0; | |
102 for (y = 0; y < 256; y++) | |
103 { | |
104 for (x = 0; x < 256; x++, value++) | |
105 { | |
106 buffer[value] = value; | |
107 } | |
108 } | |
109 | |
110 OrthancPluginCompressAndAnswerPngImage(context, output, OrthancPluginPixelFormat_Grayscale16, | |
111 256, 256, sizeof(uint16_t) * 256, buffer); | |
112 } | |
113 | |
114 return OrthancPluginErrorCode_Success; | |
115 } | |
116 | |
117 | |
118 OrthancPluginErrorCode Callback3(OrthancPluginRestOutput* output, | |
119 const char* url, | |
120 const OrthancPluginHttpRequest* request) | |
121 { | |
122 if (request->method != OrthancPluginHttpMethod_Get) | |
123 { | |
124 OrthancPluginSendMethodNotAllowed(context, output, "GET"); | |
125 } | |
126 else | |
127 { | |
128 OrthancPluginMemoryBuffer dicom; | |
129 if (!OrthancPluginGetDicomForInstance(context, &dicom, request->groups[0])) | |
130 { | |
131 /* No error, forward the DICOM file */ | |
132 OrthancPluginAnswerBuffer(context, output, dicom.data, dicom.size, "application/dicom"); | |
133 | |
134 /* Free memory */ | |
135 OrthancPluginFreeMemoryBuffer(context, &dicom); | |
136 } | |
137 } | |
138 | |
139 return OrthancPluginErrorCode_Success; | |
140 } | |
141 | |
142 | |
143 OrthancPluginErrorCode Callback4(OrthancPluginRestOutput* output, | |
144 const char* url, | |
145 const OrthancPluginHttpRequest* request) | |
146 { | |
147 /* Answer with a sample 8bpp image. */ | |
148 | |
149 uint8_t buffer[256 * 256]; | |
150 uint32_t x, y, value; | |
151 | |
152 if (request->method != OrthancPluginHttpMethod_Get) | |
153 { | |
154 OrthancPluginSendMethodNotAllowed(context, output, "GET"); | |
155 } | |
156 else | |
157 { | |
158 value = 0; | |
159 for (y = 0; y < 256; y++) | |
160 { | |
161 for (x = 0; x < 256; x++, value++) | |
162 { | |
163 buffer[value] = x; | |
164 } | |
165 } | |
166 | |
167 OrthancPluginCompressAndAnswerPngImage(context, output, OrthancPluginPixelFormat_Grayscale8, | |
168 256, 256, 256, buffer); | |
169 } | |
170 | |
171 return OrthancPluginErrorCode_Success; | |
172 } | |
173 | |
174 | |
175 OrthancPluginErrorCode Callback5(OrthancPluginRestOutput* output, | |
176 const char* url, | |
177 const OrthancPluginHttpRequest* request) | |
178 { | |
179 /** | |
180 * Demonstration the difference between the | |
181 * "OrthancPluginRestApiXXX()" and the | |
182 * "OrthancPluginRestApiXXXAfterPlugins()" mechanisms to forward | |
183 * REST calls. | |
184 * | |
185 * # curl http://localhost:8042/forward/built-in/system | |
186 * # curl http://localhost:8042/forward/plugins/system | |
187 * # curl http://localhost:8042/forward/built-in/plugin/image | |
188 * => FAILURE (because the "/plugin/image" URI is implemented by this plugin) | |
189 * # curl http://localhost:8042/forward/plugins/plugin/image => SUCCESS | |
190 **/ | |
191 | |
192 OrthancPluginMemoryBuffer tmp; | |
193 int isBuiltIn, error; | |
194 | |
195 if (request->method != OrthancPluginHttpMethod_Get) | |
196 { | |
197 OrthancPluginSendMethodNotAllowed(context, output, "GET"); | |
198 return OrthancPluginErrorCode_Success; | |
199 } | |
200 | |
201 isBuiltIn = strcmp("plugins", request->groups[0]); | |
202 | |
203 if (isBuiltIn) | |
204 { | |
205 error = OrthancPluginRestApiGet(context, &tmp, request->groups[1]); | |
206 } | |
207 else | |
208 { | |
209 error = OrthancPluginRestApiGetAfterPlugins(context, &tmp, request->groups[1]); | |
210 } | |
211 | |
212 if (error) | |
213 { | |
214 return OrthancPluginErrorCode_InternalError; | |
215 } | |
216 else | |
217 { | |
218 OrthancPluginAnswerBuffer(context, output, tmp.data, tmp.size, "application/octet-stream"); | |
219 OrthancPluginFreeMemoryBuffer(context, &tmp); | |
220 return OrthancPluginErrorCode_Success; | |
221 } | |
222 } | |
223 | |
224 | |
225 OrthancPluginErrorCode CallbackCreateDicom(OrthancPluginRestOutput* output, | |
226 const char* url, | |
227 const OrthancPluginHttpRequest* request) | |
228 { | |
229 const char* pathLocator = "\"Path\" : \""; | |
230 char info[1024]; | |
231 char *id, *eos; | |
232 OrthancPluginMemoryBuffer tmp; | |
233 | |
234 if (request->method != OrthancPluginHttpMethod_Post) | |
235 { | |
236 OrthancPluginSendMethodNotAllowed(context, output, "POST"); | |
237 } | |
238 else | |
239 { | |
240 /* Make POST request to create a new DICOM instance */ | |
241 sprintf(info, "{\"PatientName\":\"Test\"}"); | |
242 OrthancPluginRestApiPost(context, &tmp, "/tools/create-dicom", info, strlen(info)); | |
243 | |
244 /** | |
245 * Recover the ID of the created instance is constructed by a | |
246 * quick-and-dirty parsing of a JSON string. | |
247 **/ | |
248 id = strstr((char*) tmp.data, pathLocator) + strlen(pathLocator); | |
249 eos = strchr(id, '\"'); | |
250 eos[0] = '\0'; | |
251 | |
252 /* Delete the newly created DICOM instance. */ | |
253 OrthancPluginRestApiDelete(context, id); | |
254 OrthancPluginFreeMemoryBuffer(context, &tmp); | |
255 | |
256 /* Set some cookie */ | |
257 OrthancPluginSetCookie(context, output, "hello", "world"); | |
258 | |
259 /* Set some HTTP header */ | |
260 OrthancPluginSetHttpHeader(context, output, "Cache-Control", "max-age=0, no-cache"); | |
261 | |
262 OrthancPluginAnswerBuffer(context, output, "OK\n", 3, "text/plain"); | |
263 } | |
264 | |
265 return OrthancPluginErrorCode_Success; | |
266 } | |
267 | |
268 | |
269 void DicomWebBinaryCallback( | |
270 OrthancPluginDicomWebNode* node, | |
271 OrthancPluginDicomWebSetBinaryNode setter, | |
272 uint32_t levelDepth, | |
273 const uint16_t* levelTagGroup, | |
274 const uint16_t* levelTagElement, | |
275 const uint32_t* levelIndex, | |
276 uint16_t tagGroup, | |
277 uint16_t tagElement, | |
278 OrthancPluginValueRepresentation vr) | |
279 { | |
280 setter(node, OrthancPluginDicomWebBinaryMode_BulkDataUri, "HelloURI"); | |
281 } | |
282 | |
283 | |
284 OrthancPluginErrorCode OnStoredCallback(const OrthancPluginDicomInstance* instance, | |
285 const char* instanceId) | |
286 { | |
287 char buffer[256]; | |
288 FILE* fp; | |
289 char* json; | |
290 static int first = 1; | |
291 | |
292 sprintf(buffer, "Just received a DICOM instance of size %d and ID %s from origin %d (AET %s)", | |
293 (int) OrthancPluginGetInstanceSize(context, instance), instanceId, | |
294 OrthancPluginGetInstanceOrigin(context, instance), | |
295 OrthancPluginGetInstanceRemoteAet(context, instance)); | |
296 | |
297 OrthancPluginLogWarning(context, buffer); | |
298 | |
299 fp = fopen("PluginReceivedInstance.dcm", "wb"); | |
300 fwrite(OrthancPluginGetInstanceData(context, instance), | |
301 OrthancPluginGetInstanceSize(context, instance), 1, fp); | |
302 fclose(fp); | |
303 | |
304 json = OrthancPluginGetInstanceSimplifiedJson(context, instance); | |
305 if (first) | |
306 { | |
307 printf("[%s]\n", json); | |
308 } | |
309 OrthancPluginFreeString(context, json); | |
310 | |
311 if (OrthancPluginHasInstanceMetadata(context, instance, "ReceptionDate")) | |
312 { | |
313 printf("Received on [%s]\n", OrthancPluginGetInstanceMetadata(context, instance, "ReceptionDate")); | |
314 } | |
315 else | |
316 { | |
317 OrthancPluginLogError(context, "Instance has no reception date, should never happen!"); | |
318 } | |
319 | |
320 json = OrthancPluginEncodeDicomWebXml(context, | |
321 OrthancPluginGetInstanceData(context, instance), | |
322 OrthancPluginGetInstanceSize(context, instance), | |
323 DicomWebBinaryCallback); | |
324 if (first) | |
325 { | |
326 printf("[%s]\n", json); | |
327 first = 0; /* Only print the first DICOM instance */ | |
328 } | |
329 OrthancPluginFreeString(context, json); | |
330 | |
331 | |
332 return OrthancPluginErrorCode_Success; | |
333 } | |
334 | |
335 | |
336 OrthancPluginErrorCode OnChangeCallback(OrthancPluginChangeType changeType, | |
337 OrthancPluginResourceType resourceType, | |
338 const char* resourceId) | |
339 { | |
340 char info[1024]; | |
341 | |
342 OrthancPluginMemoryBuffer tmp; | |
343 memset(&tmp, 0, sizeof(tmp)); | |
344 | |
345 sprintf(info, "Change %d on resource %s of type %d", changeType, | |
346 (resourceId == NULL ? "<none>" : resourceId), resourceType); | |
347 OrthancPluginLogWarning(context, info); | |
348 | |
349 switch (changeType) | |
350 { | |
351 case OrthancPluginChangeType_NewInstance: | |
352 { | |
353 sprintf(info, "/instances/%s/metadata/AnonymizedFrom", resourceId); | |
354 if (OrthancPluginRestApiGet(context, &tmp, info) == 0) | |
355 { | |
356 sprintf(info, " Instance %s comes from the anonymization of instance", resourceId); | |
357 strncat(info, (const char*) tmp.data, tmp.size); | |
358 OrthancPluginLogWarning(context, info); | |
359 OrthancPluginFreeMemoryBuffer(context, &tmp); | |
360 } | |
361 | |
362 break; | |
363 } | |
364 | |
365 case OrthancPluginChangeType_OrthancStarted: | |
366 { | |
367 OrthancPluginSetMetricsValue(context, "sample_started", 1, OrthancPluginMetricsType_Default); | |
368 | |
369 /* Make REST requests to the built-in Orthanc API */ | |
370 OrthancPluginRestApiGet(context, &tmp, "/changes"); | |
371 OrthancPluginFreeMemoryBuffer(context, &tmp); | |
372 OrthancPluginRestApiGet(context, &tmp, "/changes?limit=1"); | |
373 OrthancPluginFreeMemoryBuffer(context, &tmp); | |
374 | |
375 /* Play with PUT by defining a new target modality. */ | |
376 sprintf(info, "[ \"STORESCP\", \"localhost\", 2000 ]"); | |
377 OrthancPluginRestApiPut(context, &tmp, "/modalities/demo", info, strlen(info)); | |
378 | |
379 break; | |
380 } | |
381 | |
382 case OrthancPluginChangeType_OrthancStopped: | |
383 OrthancPluginLogWarning(context, "Orthanc has stopped"); | |
384 break; | |
385 | |
386 default: | |
387 break; | |
388 } | |
389 | |
390 return OrthancPluginErrorCode_Success; | |
391 } | |
392 | |
393 | |
394 int32_t FilterIncomingHttpRequest(OrthancPluginHttpMethod method, | |
395 const char* uri, | |
396 const char* ip, | |
397 uint32_t headersCount, | |
398 const char* const* headersKeys, | |
399 const char* const* headersValues) | |
400 { | |
401 uint32_t i; | |
402 | |
403 if (headersCount > 0) | |
404 { | |
405 OrthancPluginLogInfo(context, "HTTP headers of an incoming REST request:"); | |
406 for (i = 0; i < headersCount; i++) | |
407 { | |
408 char info[1024]; | |
409 sprintf(info, " %s: %s", headersKeys[i], headersValues[i]); | |
410 OrthancPluginLogInfo(context, info); | |
411 } | |
412 } | |
413 | |
414 if (method == OrthancPluginHttpMethod_Get || | |
415 method == OrthancPluginHttpMethod_Post) | |
416 { | |
417 return 1; /* Allowed */ | |
418 } | |
419 else | |
420 { | |
421 return 0; /* Only allow GET and POST requests */ | |
422 } | |
423 } | |
424 | |
425 | |
426 static void RefreshMetrics() | |
427 { | |
428 static unsigned int count = 0; | |
429 OrthancPluginSetMetricsValue(context, "sample_counter", | |
430 (float) (count++), OrthancPluginMetricsType_Default); | |
431 } | |
432 | |
433 | |
434 static int32_t FilterIncomingDicomInstance(const OrthancPluginDicomInstance* instance) | |
435 { | |
436 char buf[1024]; | |
437 char* s; | |
438 int32_t hasPixelData; | |
439 | |
440 s = OrthancPluginGetInstanceTransferSyntaxUid(context, instance); | |
441 sprintf(buf, "Incoming transfer syntax: %s", s); | |
442 OrthancPluginFreeString(context, s); | |
443 OrthancPluginLogWarning(context, buf); | |
444 | |
445 hasPixelData = OrthancPluginHasInstancePixelData(context, instance); | |
446 sprintf(buf, "Incoming has pixel data: %d", hasPixelData); | |
447 OrthancPluginLogWarning(context, buf); | |
448 | |
449 /* Reject all instances without pixel data */ | |
450 return hasPixelData; | |
451 } | |
452 | |
453 | |
454 ORTHANC_PLUGINS_API int32_t OrthancPluginInitialize(OrthancPluginContext* c) | |
455 { | |
456 char info[1024], *s; | |
457 int counter, i; | |
458 OrthancPluginDictionaryEntry entry; | |
459 | |
460 context = c; | |
461 OrthancPluginLogWarning(context, "Sample plugin is initializing"); | |
462 | |
463 /* Check the version of the Orthanc core */ | |
464 if (OrthancPluginCheckVersion(c) == 0) | |
465 { | |
466 sprintf(info, "Your version of Orthanc (%s) must be above %d.%d.%d to run this plugin", | |
467 c->orthancVersion, | |
468 ORTHANC_PLUGINS_MINIMAL_MAJOR_NUMBER, | |
469 ORTHANC_PLUGINS_MINIMAL_MINOR_NUMBER, | |
470 ORTHANC_PLUGINS_MINIMAL_REVISION_NUMBER); | |
471 OrthancPluginLogError(context, info); | |
472 return -1; | |
473 } | |
474 | |
475 /* Print some information about Orthanc */ | |
476 sprintf(info, "The version of Orthanc is '%s'", context->orthancVersion); | |
477 OrthancPluginLogWarning(context, info); | |
478 | |
479 s = OrthancPluginGetOrthancPath(context); | |
480 sprintf(info, " Path to Orthanc: %s", s); | |
481 OrthancPluginLogWarning(context, info); | |
482 OrthancPluginFreeString(context, s); | |
483 | |
484 s = OrthancPluginGetOrthancDirectory(context); | |
485 sprintf(info, " Directory of Orthanc: %s", s); | |
486 OrthancPluginLogWarning(context, info); | |
487 OrthancPluginFreeString(context, s); | |
488 | |
489 s = OrthancPluginGetConfiguration(context); | |
490 sprintf(info, " Content of the configuration file:\n"); | |
491 OrthancPluginLogWarning(context, info); | |
492 OrthancPluginLogWarning(context, s); | |
493 OrthancPluginFreeString(context, s); | |
494 | |
495 /* Print the command-line arguments of Orthanc */ | |
496 counter = OrthancPluginGetCommandLineArgumentsCount(context); | |
497 for (i = 0; i < counter; i++) | |
498 { | |
499 s = OrthancPluginGetCommandLineArgument(context, i); | |
500 sprintf(info, " Command-line argument %d: \"%s\"", i, s); | |
501 OrthancPluginLogWarning(context, info); | |
502 OrthancPluginFreeString(context, s); | |
503 } | |
504 | |
505 /* Register the callbacks */ | |
506 OrthancPluginRegisterRestCallback(context, "/(plu.*)/hello", Callback1); | |
507 OrthancPluginRegisterRestCallback(context, "/plu.*/image", Callback2); | |
508 OrthancPluginRegisterRestCallback(context, "/plugin/instances/([^/]+)/info", Callback3); | |
509 OrthancPluginRegisterRestCallback(context, "/instances/([^/]+)/preview", Callback4); | |
510 OrthancPluginRegisterRestCallback(context, "/forward/(built-in)(/.+)", Callback5); | |
511 OrthancPluginRegisterRestCallback(context, "/forward/(plugins)(/.+)", Callback5); | |
512 OrthancPluginRegisterRestCallback(context, "/plugin/create", CallbackCreateDicom); | |
513 | |
514 OrthancPluginRegisterOnStoredInstanceCallback(context, OnStoredCallback); | |
515 OrthancPluginRegisterOnChangeCallback(context, OnChangeCallback); | |
516 OrthancPluginRegisterIncomingHttpRequestFilter(context, FilterIncomingHttpRequest); | |
517 OrthancPluginRegisterRefreshMetricsCallback(context, RefreshMetrics); | |
518 OrthancPluginRegisterIncomingDicomInstanceFilter(context, FilterIncomingDicomInstance); | |
519 | |
520 | |
521 /* Declare several properties of the plugin */ | |
522 OrthancPluginSetRootUri(context, "/plugin/hello"); | |
523 OrthancPluginSetDescription(context, "This is the description of the sample plugin that can be seen in Orthanc Explorer."); | |
524 OrthancPluginExtendOrthancExplorer(context, "alert('Hello Orthanc! From sample plugin with love.');"); | |
525 | |
526 customError = OrthancPluginRegisterErrorCode(context, 4, 402, "Hello world"); | |
527 | |
528 OrthancPluginRegisterDictionaryTag(context, 0x0014, 0x1020, OrthancPluginValueRepresentation_DA, | |
529 "ValidationExpiryDate", 1, 1); | |
530 | |
531 OrthancPluginLookupDictionary(context, &entry, "ValidationExpiryDate"); | |
532 OrthancPluginLookupDictionary(context, &entry, "0010-0010"); | |
533 | |
534 return 0; | |
535 } | |
536 | |
537 | |
538 ORTHANC_PLUGINS_API void OrthancPluginFinalize() | |
539 { | |
540 OrthancPluginLogWarning(context, "Sample plugin is finalizing"); | |
541 } | |
542 | |
543 | |
544 ORTHANC_PLUGINS_API const char* OrthancPluginGetName() | |
545 { | |
546 return "sample"; | |
547 } | |
548 | |
549 | |
550 ORTHANC_PLUGINS_API const char* OrthancPluginGetVersion() | |
551 { | |
552 return "1.0"; | |
553 } | |
554 |