NanoPlugin - mbarnig/RadioLogic GitHub Wiki
Welcome to the RadioLogic tutorial lesson 10. In this lesson we will develop a very simple plugin to add to our RadioLogicArchive. The plugin returns a webpage displaying the message "Moien Orthanc" when we call the URL http://<RadioLogicArchive IP>:8042/moien. "Moien" is the luxembourgish word for "Hello".
This call can be established inside your browser or with the curl command inside a terminal.
There are three ways to develop an Orthanc plugin with the Orthanc SDK. They are presented in detail in the chapter Evolution of the Plugin Code. You don't need to read this description if you don't want to become a specialist.
You probably know that a specialist knows everything about nothing, whereas a generalist knows nothing about everything. For the generalists following this tutorial it is sufficient to know that we will use the most complex way to develop our first plugin. We will download and configure the so-called Orthanc Framework. If you think that choosing this option to design a program that only says "Hello" is overkill, you are right. But we will not only develop a first plugin, but a second, a third, and even more. For these additional plugins we need the enhanced features that the Orthanc framework offers. What we learn in the present lesson is useful for the next lessons. That's the main reason why we start with a rather complex code.
The main component is the C++ program. The code is contained in the file NanoPlugin.cpp, saved in the folder /OrthancPlugins/NanoPlugin/ in the present repository.
If you prefer paper-based-learning, instead of screen-based-learning, the code is copied below to facilitate the impression of the present lesson:
#include <Plugins/Samples/Common/OrthancPluginCppWrapper.h>
#include <Core/SystemToolbox.h>
#include <Core/Toolbox.h>
#include <Core/Logging.h>
#include <string>
void Moien(OrthancPluginRestOutput* output,
const char* url, const OrthancPluginHttpRequest* request) {
OrthancPluginContext* context = OrthancPlugins::GetGlobalContext();
std::string HtmlCode = "<html>\n<head>\n<title>NanoPlugin</title>\n</head>\n<body>\n<h2>Moien Orthanc</h2>\n</body>\n</html>\n";
OrthancPluginAnswerBuffer(context, output, HtmlCode.c_str(), HtmlCode.length(), "text/html");
}
extern "C"
{
ORTHANC_PLUGINS_API int32_t OrthancPluginInitialize(OrthancPluginContext* context)
{
Orthanc::Logging::Initialize(context);
OrthancPlugins::SetGlobalContext(context);
OrthancPluginSetDescription(context, "Orthanc Plugin Test with Framework; do HTTP GET call to <RadioLogicArchive IP>/moien");
OrthancPlugins::RegisterRestCallback<Moien>("/moien", true);
return 0;
}
ORTHANC_PLUGINS_API void OrthancPluginFinalize()
{
LOG(INFO) << "NanoPlugin is finalizing";
}
ORTHANC_PLUGINS_API const char* OrthancPluginGetName()
{
return "NanoPlugin";
}
ORTHANC_PLUGINS_API const char* OrthancPluginGetVersion()
{
return "1.0.0";
}
} Let's first explore the extern "C" section of the Orthanc SDK which exposes 4 public functions:
- int32_t OrthancPluginInitialize(const OrthancPluginContext* context)
- void OrthancPluginFinalize()
- const char* OrthancPluginGetName()
- const char* OrthancPluginGetVersion()
The first function initializes the internal Logger, sets the global context, defines the description of the plugin and registrates the callback function Moien. The second function adds the message "NanoPlugin is finalizing" to the log file. The third function defines the plugin name as "NanoPlugin". The last function sets the plugin version to 1.0.0. That's all.
The code of the registrated callback function Moien is above the extern "C" section. The data related to the incoming HTTP .../moien request is provided as parameters to this function. A very simple HTML webpage is assigned to a string which is returned by the SDK function OrthancPluginAnswerBuffer.
At the top of the C++ script are some include files to embed the needed Orthanc source files for the plugin.
To build the plugin we need a second file which is part of the Orthanc source code and is located inside the NanoPlugin/Resources/ folder. It's a python script named SyncOrthancFolder.py which downloads and updates the Orthanc source code inside our development environment to match the latest version. Explaining this script is out of scope of this small tutorial and the code analysis is reserved to the specialists among the readers.
A third file, called CMakeLists.txt, is part of CMake, an open-source, cross-platform family of tools designed to build, test and package software. This file provides instructions to the compiler and linker of the development system how to assemble all the source code and how to build the plugin. For convenience the CMakeLists.txt file is also copied hereafter:
cmake_minimum_required(VERSION 2.8)
project(NanoPlugin)
set(ORTHANC_FRAMEWORK_VERSION "mainline")
set(ORTHANC_FRAMEWORK_SOURCE "hg" CACHE STRING "Source of the Orthanc source code (can be hg, archive, web or path")
set(USE_SYSTEM_ORTHANC_SDK ON CACHE BOOL "Use the system version of the Orthanc plugin SDK")
execute_process(
COMMAND
/usr/bin/python ${CMAKE_SOURCE_DIR}/Resources/SyncOrthancFolder.py
)
include(${CMAKE_SOURCE_DIR}/Resources/Orthanc/DownloadOrthancFramework.cmake)
set(ORTHANC_FRAMEWORK_PLUGIN ON)
include(${ORTHANC_ROOT}/Resources/CMake/OrthancFrameworkParameters.cmake)
set(ENABLE_LOCALE ON)
set(ENABLE_MODULE_IMAGES OFF CACHE INTERNAL "Enable module for image processing")
set(ENABLE_MODULE_JOBS OFF CACHE INTERNAL "Enable module for jobs")
set(ENABLE_MODULE_DICOM OFF CACHE INTERNAL "Enable module for DICOM handling")
include(${ORTHANC_ROOT}/Resources/CMake/OrthancFrameworkConfiguration.cmake)
include_directories(
${ORTHANC_ROOT}
${ORTHANC_ROOT}/Core
${ORTHANC_ROOT}/Plugins/Include
)
add_definitions(
-DHAS_ORTHANC_EXCEPTION=1
-DORTHANC_ENABLE_LOGGING_PLUGIN=1
)
set(CORE_SOURCES
${ORTHANC_ROOT}/Plugins/Samples/Common/OrthancPluginCppWrapper.cpp
${ORTHANC_CORE_SOURCES}
)
add_library(NanoPlugin SHARED
${CORE_SOURCES}
${CMAKE_SOURCE_DIR}/NanoPlugin.cpp
${AUTOGENERATED_SOURCES}
) Bottom up. We explore first the bottom of the CMake script. The command add_library specifies how to build a library with the name NanoPlugin by sharing three sources. Among them the CORE_SOURCES which are defined in the second-last-command. Going up step by step to the top of the CMakeLists.txt file we see that two definitions have been added and three directories have been included into the build.
The preceding commands are a key sequence. The CMake script OrthancFrameworkParameters.cmake sets numerous parameters to default values, which are modified with set commands and applied with OrthancFrameworkConfiguration.cmake.
The python script SyncOrthancFolder.py is run with the command execute_process(COMMAND...), then the framework is downloaded and enabled. The top commands are setting the name of the project, the versions and source of the framework and the version of the SDK.
The code and scripts are now ready and the reader hopefully understands what they are doing. Question: how to progress ? We must install the files inside our development environment and launch the build process.
If you followed this tutorial lesson by lesson you remember that we created a Docker image called dev-snapshot which contains the linux development tools. To login to our Docker system we enter the known commands in our SSH shell and check if the dev-snapshot image is still present.
docker images

If not, you are not lost. I saved this image to the DockerHub with the name mbarnig/radiologic-development. I explained earlier how to download an image from the DockerHub. The command to create and start the container is very simple:
docker container run -it --name RadioLogicDevelopment dev-snapshot /bin/bash
The lazy readers who used the development image from the DockerHub must replace the image name dev-snapshot in the above command by the name mbarnig/radiologic-development.
We create a new directory at container root with the command
mkdir PluginDevelopment
cd PluginDevelopmentIn lesson 3 you learned how to share data between the host and the container. Try to install the folder NanoPlugin inside the new created directory. If the installation fails, you can use an emergency solution and get the files from the website web3.lu with the commands, line per line
wget https://www.web3.lu/downloads/NanoPlugin.zip
unzip NanoPlugin.zip
rm NanoPlugin.zip
cd NanoPlugin In lesson 4 you learned how build Orthanc with make. Building a plugin is similar. Here are the steps:
mkdir Build
cd Build
cmake -DSTATIC_BUILD=ON -DCMAKE_BUILD_TYPE=Release ../
make And the result is ?

Yuppy! Everything worked as expected.
Now we stop the container with Ctrl c and copy the plugin library into the shared plugins folder of the RadioLogicArchive container with the single command (one line)
docker cp RadioLogicDevelopment:/PluginDevelopment/NanoPlugin/Build/libNanoPlugin.so /volume1/radiologicarchive/plugins/libNanoPlugin.soIf the RadioLogicArchive is still running, we stop the container
docker stop RadioLogicArchive
When restarting the container
docker restart RadioLogicArchive
the NanoPlugin is loaded and visible in the list of Orthanc plugins in the server.

We change the URL-suffix /app/explorer.html with /moien in the browser and enjoy the fruits of lesson 10:

This was hard work. After a short break we are going to create our second plugin. Instead of simply saying "Hello" we will execute a useful task. Scaling a DICOM image is our goal. If you like this idea, go to lesson 11 > MicroPlugin.